Merge "Default to not allowing cleartext traffic for ephemeral apps"
diff --git a/Android.bp b/Android.bp
index dba49ce..9088315 100644
--- a/Android.bp
+++ b/Android.bp
@@ -17,3 +17,7 @@
     "native/android",
     "native/graphics/jni",
 ]
+
+optional_subdirs = [
+    "core/tests/utiltests/jni",
+]
diff --git a/Android.mk b/Android.mk
index 7099848..e17fd0b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -494,6 +494,7 @@
 
 LOCAL_SRC_FILES += \
 	../../system/netd/server/binder/android/net/INetd.aidl \
+	../native/cmds/installd/binder/android/os/IInstalld.aidl \
 
 LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
 
@@ -519,7 +520,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(framework_res_R_stamp)
 
 LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp legacy-test bouncycastle ext
+LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp bouncycastle ext
 
 LOCAL_STATIC_JAVA_LIBRARIES :=                          \
     framework-protos                                    \
@@ -668,6 +669,9 @@
 	frameworks/base/core/java/android/view/textservice/SuggestionsInfo.aidl \
 	frameworks/base/core/java/android/service/carrier/CarrierIdentifier.aidl \
 	frameworks/base/core/java/android/service/carrier/MessagePdu.aidl \
+	frameworks/base/core/java/android/service/notification/Adjustment.aidl \
+	frameworks/base/core/java/android/service/notification/Condition.aidl \
+	frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \
 	frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
 	frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
 	frameworks/base/core/java/android/speech/tts/Voice.aidl \
@@ -757,6 +761,7 @@
 # Search through the base framework dirs for these packages.
 # The result will be relative to frameworks/base.
 fwbase_dirs_to_document := \
+	legacy-test/src \
 	test-runner/src \
 	$(patsubst $(LOCAL_PATH)/%,%, \
 	  $(wildcard \
diff --git "a/\135" "b/\135"
deleted file mode 100644
index 5619151..0000000
--- "a/\135"
+++ /dev/null
@@ -1,12 +0,0 @@
-NetworkNotificationManager: logging improvements
-
-TODO: squash me
-# Please enter the commit message for your changes. Lines starting
-# with '#' will be ignored, and an empty message aborts the commit.
-# On branch notification_tagging
-# Your branch is ahead of 'goog/master' by 2 commits.
-#   (use "git push" to publish your local commits)
-#
-# Changes to be committed:
-#	modified:   services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
-#
diff --git a/api/current.txt b/api/current.txt
index d158e10..c294d8a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3479,6 +3479,7 @@
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
     method public void enterPictureInPictureMode(float);
+    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -7977,6 +7978,7 @@
     method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
     method protected final void setPathPermissions(android.content.pm.PathPermission[]);
     method protected final void setReadPermission(java.lang.String);
@@ -8009,6 +8011,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
     method public deprecated boolean release();
     method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
@@ -8111,6 +8114,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public final boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
     method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
     method public void releasePersistableUriPermission(android.net.Uri, int);
@@ -8133,6 +8137,9 @@
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
     field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
     field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
+    field public static final java.lang.String QUERY_ARG_SELECTION = "android:query-selection";
+    field public static final java.lang.String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
+    field public static final java.lang.String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
     field public static final java.lang.String SCHEME_CONTENT = "content";
     field public static final java.lang.String SCHEME_FILE = "file";
@@ -35091,18 +35098,19 @@
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
-    ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri, int);
+    ctor public Adjustment(java.lang.String, java.lang.String, android.os.Bundle, java.lang.CharSequence, int);
     ctor protected Adjustment(android.os.Parcel);
     method public int describeContents();
     method public java.lang.CharSequence getExplanation();
-    method public int getImportance();
     method public java.lang.String getKey();
     method public java.lang.String getPackage();
-    method public android.net.Uri getReference();
     method public android.os.Bundle getSignals();
     method public int getUser();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+    field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
+    field public static final java.lang.String KEY_PEOPLE = "key_people";
+    field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
   }
 
   public final class Condition implements android.os.Parcelable {
@@ -35224,11 +35232,14 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public java.util.List<java.lang.String> getAdditionalPeople();
+    method public android.app.NotificationChannel getChannel();
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
+    method public java.util.List<android.service.notification.SnoozeCriterion> getSnoozeCriteria();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
@@ -35242,6 +35253,17 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public final class SnoozeCriterion implements android.os.Parcelable {
+    ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
+    ctor protected SnoozeCriterion(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.CharSequence getConfirmation();
+    method public java.lang.CharSequence getExplanation();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
+  }
+
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public deprecated StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -37294,7 +37316,9 @@
     field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
     field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
     field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
+    field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
     field public static final java.lang.String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+    field public static final java.lang.String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
     field public static final java.lang.String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL = "mdn_is_additional_voicemail_number_bool";
     field public static final java.lang.String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
     field public static final java.lang.String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
@@ -43504,6 +43528,7 @@
     field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
     field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
     field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
+    field public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
     field public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
@@ -62606,13 +62631,13 @@
     ctor public Attributes.Name(java.lang.String);
     field public static final java.util.jar.Attributes.Name CLASS_PATH;
     field public static final java.util.jar.Attributes.Name CONTENT_TYPE;
-    field public static final java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
+    field public static final deprecated java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
     field public static final java.util.jar.Attributes.Name EXTENSION_LIST;
     field public static final java.util.jar.Attributes.Name EXTENSION_NAME;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_TITLE;
-    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_URL;
+    field public static final deprecated java.util.jar.Attributes.Name IMPLEMENTATION_URL;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR;
-    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
+    field public static final deprecated java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VERSION;
     field public static final java.util.jar.Attributes.Name MAIN_CLASS;
     field public static final java.util.jar.Attributes.Name MANIFEST_VERSION;
@@ -62679,11 +62704,11 @@
   }
 
   public static abstract interface Pack200.Packer {
-    method public abstract void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract void pack(java.util.jar.JarFile, java.io.OutputStream) throws java.io.IOException;
     method public abstract void pack(java.util.jar.JarInputStream, java.io.OutputStream) throws java.io.IOException;
     method public abstract java.util.SortedMap<java.lang.String, java.lang.String> properties();
-    method public abstract void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener);
     field public static final java.lang.String CLASS_ATTRIBUTE_PFX = "pack.class.attribute.";
     field public static final java.lang.String CODE_ATTRIBUTE_PFX = "pack.code.attribute.";
     field public static final java.lang.String DEFLATE_HINT = "pack.deflate.hint";
@@ -62706,9 +62731,9 @@
   }
 
   public static abstract interface Pack200.Unpacker {
-    method public abstract void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract java.util.SortedMap<java.lang.String, java.lang.String> properties();
-    method public abstract void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract void unpack(java.io.InputStream, java.util.jar.JarOutputStream) throws java.io.IOException;
     method public abstract void unpack(java.io.File, java.util.jar.JarOutputStream) throws java.io.IOException;
     field public static final java.lang.String DEFLATE_HINT = "unpack.deflate.hint";
diff --git a/api/system-current.txt b/api/system-current.txt
index 9d48713..a3672b3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3596,6 +3596,7 @@
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
     method public void enterPictureInPictureMode(float);
+    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -8312,6 +8313,7 @@
     method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
     method protected final void setPathPermissions(android.content.pm.PathPermission[]);
     method protected final void setReadPermission(java.lang.String);
@@ -8344,6 +8346,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
     method public deprecated boolean release();
     method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
@@ -8446,6 +8449,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public final boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
     method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
     method public void releasePersistableUriPermission(android.net.Uri, int);
@@ -8468,6 +8472,9 @@
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
     field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
     field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
+    field public static final java.lang.String QUERY_ARG_SELECTION = "android:query-selection";
+    field public static final java.lang.String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
+    field public static final java.lang.String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
     field public static final java.lang.String SCHEME_CONTENT = "content";
     field public static final java.lang.String SCHEME_FILE = "file";
@@ -10235,6 +10242,7 @@
     method public void setAppPackageName(java.lang.String);
     method public void setDontKillApp(boolean);
     method public void setGrantedRuntimePermissions(java.lang.String[]);
+    method public void setInstallAsInstantApp(boolean);
     method public void setInstallLocation(int);
     method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
@@ -10527,6 +10535,7 @@
     field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
     field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ALL = 131072; // 0x20000
+    field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
     field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
@@ -37922,18 +37931,19 @@
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
-    ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri, int);
+    ctor public Adjustment(java.lang.String, java.lang.String, android.os.Bundle, java.lang.CharSequence, int);
     ctor protected Adjustment(android.os.Parcel);
     method public int describeContents();
     method public java.lang.CharSequence getExplanation();
-    method public int getImportance();
     method public java.lang.String getKey();
     method public java.lang.String getPackage();
-    method public android.net.Uri getReference();
     method public android.os.Bundle getSignals();
     method public int getUser();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+    field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
+    field public static final java.lang.String KEY_PEOPLE = "key_people";
+    field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
   }
 
   public final class Condition implements android.os.Parcelable {
@@ -38062,11 +38072,14 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public java.util.List<java.lang.String> getAdditionalPeople();
+    method public android.app.NotificationChannel getChannel();
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
+    method public java.util.List<android.service.notification.SnoozeCriterion> getSnoozeCriteria();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
@@ -38080,6 +38093,17 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public final class SnoozeCriterion implements android.os.Parcelable {
+    ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
+    ctor protected SnoozeCriterion(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.CharSequence getConfirmation();
+    method public java.lang.CharSequence getExplanation();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
+  }
+
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public deprecated StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -40398,7 +40422,9 @@
     field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
     field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
     field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
+    field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
     field public static final java.lang.String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+    field public static final java.lang.String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
     field public static final java.lang.String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL = "mdn_is_additional_voicemail_number_bool";
     field public static final java.lang.String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
     field public static final java.lang.String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
@@ -46690,6 +46716,7 @@
     field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
     field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
     field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
+    field public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
     field public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
@@ -66151,13 +66178,13 @@
     ctor public Attributes.Name(java.lang.String);
     field public static final java.util.jar.Attributes.Name CLASS_PATH;
     field public static final java.util.jar.Attributes.Name CONTENT_TYPE;
-    field public static final java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
+    field public static final deprecated java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
     field public static final java.util.jar.Attributes.Name EXTENSION_LIST;
     field public static final java.util.jar.Attributes.Name EXTENSION_NAME;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_TITLE;
-    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_URL;
+    field public static final deprecated java.util.jar.Attributes.Name IMPLEMENTATION_URL;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR;
-    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
+    field public static final deprecated java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VERSION;
     field public static final java.util.jar.Attributes.Name MAIN_CLASS;
     field public static final java.util.jar.Attributes.Name MANIFEST_VERSION;
@@ -66224,11 +66251,11 @@
   }
 
   public static abstract interface Pack200.Packer {
-    method public abstract void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract void pack(java.util.jar.JarFile, java.io.OutputStream) throws java.io.IOException;
     method public abstract void pack(java.util.jar.JarInputStream, java.io.OutputStream) throws java.io.IOException;
     method public abstract java.util.SortedMap<java.lang.String, java.lang.String> properties();
-    method public abstract void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener);
     field public static final java.lang.String CLASS_ATTRIBUTE_PFX = "pack.class.attribute.";
     field public static final java.lang.String CODE_ATTRIBUTE_PFX = "pack.code.attribute.";
     field public static final java.lang.String DEFLATE_HINT = "pack.deflate.hint";
@@ -66251,9 +66278,9 @@
   }
 
   public static abstract interface Pack200.Unpacker {
-    method public abstract void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract java.util.SortedMap<java.lang.String, java.lang.String> properties();
-    method public abstract void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract void unpack(java.io.InputStream, java.util.jar.JarOutputStream) throws java.io.IOException;
     method public abstract void unpack(java.io.File, java.util.jar.JarOutputStream) throws java.io.IOException;
     field public static final java.lang.String DEFLATE_HINT = "unpack.deflate.hint";
diff --git a/api/test-current.txt b/api/test-current.txt
index 9cd08d5..11f1c5c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3481,6 +3481,7 @@
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
     method public void enterPictureInPictureMode(float);
+    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -7999,6 +8000,7 @@
     method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
     method protected final void setPathPermissions(android.content.pm.PathPermission[]);
     method protected final void setReadPermission(java.lang.String);
@@ -8031,6 +8033,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
     method public deprecated boolean release();
     method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
@@ -8134,6 +8137,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public final boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
     method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
     method public void releasePersistableUriPermission(android.net.Uri, int);
@@ -8156,6 +8160,9 @@
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
     field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
     field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
+    field public static final java.lang.String QUERY_ARG_SELECTION = "android:query-selection";
+    field public static final java.lang.String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
+    field public static final java.lang.String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
     field public static final java.lang.String SCHEME_CONTENT = "content";
     field public static final java.lang.String SCHEME_FILE = "file";
@@ -35188,18 +35195,19 @@
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
-    ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri, int);
+    ctor public Adjustment(java.lang.String, java.lang.String, android.os.Bundle, java.lang.CharSequence, int);
     ctor protected Adjustment(android.os.Parcel);
     method public int describeContents();
     method public java.lang.CharSequence getExplanation();
-    method public int getImportance();
     method public java.lang.String getKey();
     method public java.lang.String getPackage();
-    method public android.net.Uri getReference();
     method public android.os.Bundle getSignals();
     method public int getUser();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+    field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
+    field public static final java.lang.String KEY_PEOPLE = "key_people";
+    field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
   }
 
   public final class Condition implements android.os.Parcelable {
@@ -35321,11 +35329,14 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public java.util.List<java.lang.String> getAdditionalPeople();
+    method public android.app.NotificationChannel getChannel();
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
+    method public java.util.List<android.service.notification.SnoozeCriterion> getSnoozeCriteria();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
@@ -35339,6 +35350,17 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public final class SnoozeCriterion implements android.os.Parcelable {
+    ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
+    ctor protected SnoozeCriterion(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.CharSequence getConfirmation();
+    method public java.lang.CharSequence getExplanation();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
+  }
+
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public deprecated StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -37391,7 +37413,9 @@
     field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
     field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
     field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
+    field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
     field public static final java.lang.String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+    field public static final java.lang.String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
     field public static final java.lang.String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL = "mdn_is_additional_voicemail_number_bool";
     field public static final java.lang.String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
     field public static final java.lang.String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
@@ -41525,11 +41549,15 @@
   public final class ProtoOutputStream {
     ctor public ProtoOutputStream();
     ctor public ProtoOutputStream(int);
+    ctor public ProtoOutputStream(java.io.OutputStream);
+    ctor public ProtoOutputStream(java.io.FileDescriptor);
     method public static int checkFieldId(long, long);
     method public static int convertObjectIdToOrdinal(int);
     method public void dump(java.lang.String);
+    method public void end(long);
     method public void endObject(long);
     method public void endRepeatedObject(long);
+    method public void flush();
     method public byte[] getBytes();
     method public static int getDepthFromToken(long);
     method public static int getObjectIdFromToken(long);
@@ -41538,9 +41566,17 @@
     method public static int getTagSizeFromToken(long);
     method public static long makeFieldId(int, long);
     method public static long makeToken(int, boolean, int, int, int);
+    method public long start(long);
     method public long startObject(long);
     method public long startRepeatedObject(long);
     method public static java.lang.String token2String(long);
+    method public void write(long, double);
+    method public void write(long, float);
+    method public void write(long, int);
+    method public void write(long, long);
+    method public void write(long, boolean);
+    method public void write(long, java.lang.String);
+    method public void write(long, byte[]);
     method public void writeBool(long, boolean);
     method public void writeBytes(long, byte[]);
     method public void writeDouble(long, double);
@@ -41550,6 +41586,8 @@
     method public void writeFloat(long, float);
     method public void writeInt32(long, int);
     method public void writeInt64(long, long);
+    method public void writeObject(long, byte[]);
+    method public void writeObjectImpl(int, byte[]);
     method public void writePackedBool(long, boolean[]);
     method public void writePackedDouble(long, double[]);
     method public void writePackedEnum(long, int[]);
@@ -41573,6 +41611,8 @@
     method public void writeRepeatedFloat(long, float);
     method public void writeRepeatedInt32(long, int);
     method public void writeRepeatedInt64(long, long);
+    method public void writeRepeatedObject(long, byte[]);
+    method public void writeRepeatedObjectImpl(int, byte[]);
     method public void writeRepeatedSFixed32(long, int);
     method public void writeRepeatedSFixed64(long, long);
     method public void writeRepeatedSInt32(long, int);
@@ -43757,6 +43797,7 @@
     field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
     field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
     field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
+    field public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
     field public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
@@ -62879,13 +62920,13 @@
     ctor public Attributes.Name(java.lang.String);
     field public static final java.util.jar.Attributes.Name CLASS_PATH;
     field public static final java.util.jar.Attributes.Name CONTENT_TYPE;
-    field public static final java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
+    field public static final deprecated java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
     field public static final java.util.jar.Attributes.Name EXTENSION_LIST;
     field public static final java.util.jar.Attributes.Name EXTENSION_NAME;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_TITLE;
-    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_URL;
+    field public static final deprecated java.util.jar.Attributes.Name IMPLEMENTATION_URL;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR;
-    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
+    field public static final deprecated java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
     field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VERSION;
     field public static final java.util.jar.Attributes.Name MAIN_CLASS;
     field public static final java.util.jar.Attributes.Name MANIFEST_VERSION;
@@ -62952,11 +62993,11 @@
   }
 
   public static abstract interface Pack200.Packer {
-    method public abstract void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract void pack(java.util.jar.JarFile, java.io.OutputStream) throws java.io.IOException;
     method public abstract void pack(java.util.jar.JarInputStream, java.io.OutputStream) throws java.io.IOException;
     method public abstract java.util.SortedMap<java.lang.String, java.lang.String> properties();
-    method public abstract void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener);
     field public static final java.lang.String CLASS_ATTRIBUTE_PFX = "pack.class.attribute.";
     field public static final java.lang.String CODE_ATTRIBUTE_PFX = "pack.code.attribute.";
     field public static final java.lang.String DEFLATE_HINT = "pack.deflate.hint";
@@ -62979,9 +63020,9 @@
   }
 
   public static abstract interface Pack200.Unpacker {
-    method public abstract void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract java.util.SortedMap<java.lang.String, java.lang.String> properties();
-    method public abstract void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public default deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener);
     method public abstract void unpack(java.io.InputStream, java.util.jar.JarOutputStream) throws java.io.IOException;
     method public abstract void unpack(java.io.File, java.util.jar.JarOutputStream) throws java.io.IOException;
     field public static final java.lang.String DEFLATE_HINT = "unpack.deflate.hint";
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 63641a8..3687f10 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.app.ContentProviderHolder;
 import android.app.IActivityManager;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.IContentProvider;
 import android.database.Cursor;
@@ -589,8 +590,8 @@
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection, mWhere,
-                    null, mSortOrder, null);
+            Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection,
+                    ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
             if (cursor == null) {
                 System.out.println("No result found.");
                 return;
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 0e0ecd0..4be4654 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -64,13 +64,15 @@
                 "       media dispatch KEY\n" +
                 "       media list-sessions\n" +
                 "       media monitor <tag>\n" +
+                "       media volume [options]\n" +
                 "\n" +
                 "media dispatch: dispatch a media key to the system.\n" +
                 "                KEY may be: play, pause, play-pause, mute, headsethook,\n" +
                 "                stop, next, previous, rewind, record, fast-forword.\n" +
                 "media list-sessions: print a list of the current sessions.\n" +
                         "media monitor: monitor updates to the specified session.\n" +
-                "                       Use the tag from list-sessions.\n"
+                "                       Use the tag from list-sessions.\n" +
+                "media volume:  " + VolumeCtrl.USAGE
         );
     }
 
@@ -92,6 +94,8 @@
             runListSessions();
         } else if (op.equals("monitor")) {
             runMonitor();
+        } else if (op.equals("volume")) {
+            runVolume();
         } else {
             showError("Error: unknown command '" + op + "'");
             return;
@@ -322,4 +326,10 @@
             System.out.println("***Error listing sessions***");
         }
     }
+
+    //=================================
+    // "volume" command for stream volume control
+    private void runVolume() throws Exception {
+        VolumeCtrl.run(this);
+    }
 }
diff --git a/cmds/media/src/com/android/commands/media/VolumeCtrl.java b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
new file mode 100755
index 0000000..f54ae59
--- /dev/null
+++ b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
@@ -0,0 +1,178 @@
+/*
+**
+** 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 com.android.commands.media;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IAudioService;
+import android.os.ServiceManager;
+import android.util.AndroidException;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.ArrayIndexOutOfBoundsException;
+
+/**
+ * Command line tool to exercise AudioService.setStreamVolume()
+ *                           and AudioService.adjustStreamVolume()
+ */
+public class VolumeCtrl {
+
+    private final static String TAG = "VolumeCtrl";
+
+    public final static String USAGE = new String(
+            "the options are as follows: \n" +
+            "\t\t--stream STREAM selects the stream to control, see AudioManager.STREAM_*\n" +
+            "\t\t                controls AudioManager.STREAM_MUSIC if no stream is specified\n"+
+            "\t\t--index INDEX   sets the volume index value\n" +
+            "\t\t--adj DIRECTION adjusts the volume, use raise|same|lower for the direction\n" +
+            "\t\t--show          shows the UI during the volume change \n" +
+            "\texamples:\n" +
+            "\t\tadb shell media volume --show --stream 3 --index 11 \n" +
+            "\t\tadb shell media volume --stream 0 --adj lower \n"
+            );
+
+    private final static int VOLUME_CONTROL_MODE_SET = 0;
+    private final static int VOLUME_CONTROL_MODE_ADJUST = 1;
+
+    private final static String ADJUST_LOWER = "lower";
+    private final static String ADJUST_SAME = "same";
+    private final static String ADJUST_RAISE = "raise";
+
+    public static void run(BaseCommand cmd) throws Exception {
+        //----------------------------------------
+        // Default parameters
+        int stream = AudioManager.STREAM_MUSIC;
+        int volIndex = 5;
+        int mode = VOLUME_CONTROL_MODE_SET;
+        int adjDir = AudioManager.ADJUST_RAISE;
+        boolean showUi = false;
+
+        //----------------------------------------
+        // read options
+        String option;
+        String adjustment = null;
+        while ((option = cmd.nextOption()) != null) {
+            switch (option) {
+                case "--show":
+                    showUi = true;
+                    break;
+                case "--set":
+                    mode = VOLUME_CONTROL_MODE_SET;
+                    log(LOG_V, "will set volume");
+                    break;
+                case "--adjust":
+                    mode = VOLUME_CONTROL_MODE_ADJUST;
+                    log(LOG_V, "will adjust volume");
+                    break;
+                case "--stream":
+                    stream = Integer.decode(cmd.nextArgRequired()).intValue();
+                    log(LOG_V, "will control stream=" + stream + " (" + streamName(stream) + ")");
+                    break;
+                case "--index":
+                    volIndex = Integer.decode(cmd.nextArgRequired()).intValue();
+                    mode = VOLUME_CONTROL_MODE_SET;
+                    log(LOG_V, "will set volume to index=" + volIndex);
+                    break;
+                case "--adj":
+                    mode = VOLUME_CONTROL_MODE_ADJUST;
+                    adjustment = cmd.nextArgRequired();
+                    log(LOG_V, "will adjust volume");
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown argument " + option);
+            }
+        }
+
+        //------------------------------
+        // Read options: validation
+        if (mode == VOLUME_CONTROL_MODE_ADJUST) {
+            if (adjustment == null) {
+                cmd.showError("Error: no valid volume adjustment (null)");
+                return;
+            }
+            switch (adjustment) {
+                case ADJUST_RAISE: adjDir = AudioManager.ADJUST_RAISE; break;
+                case ADJUST_SAME: adjDir = AudioManager.ADJUST_SAME; break;
+                case ADJUST_LOWER: adjDir = AudioManager.ADJUST_LOWER; break;
+                default:
+                    cmd.showError("Error: no valid volume adjustment, was " + adjustment
+                            + ", expected " + ADJUST_LOWER + "|" + ADJUST_SAME + "|"
+                            + ADJUST_RAISE);
+                    return;
+            }
+        }
+
+        //----------------------------------------
+        // Test initialization
+        log(LOG_V, "Connecting to AudioService");
+        IAudioService audioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
+                Context.AUDIO_SERVICE));
+        if (audioService == null) {
+            System.err.println(BaseCommand.NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException(
+                    "Can't connect to audio service; is the system running?");
+        }
+
+        if (mode == VOLUME_CONTROL_MODE_SET) {
+            if ((volIndex > audioService.getStreamMaxVolume(stream))
+                    || (volIndex < audioService.getStreamMinVolume(stream))) {
+                cmd.showError(String.format("Error: invalid volume index %d for stream %d "
+                        + "(should be in [%d..%d])", volIndex, stream,
+                        audioService.getStreamMinVolume(stream),
+                        audioService.getStreamMaxVolume(stream)));
+                return;
+            }
+        }
+
+        //----------------------------------------
+        // Non-interactive test
+        final int flag = showUi? AudioManager.FLAG_SHOW_UI : 0;
+        final String pack = cmd.getClass().getPackage().getName();
+        if (mode == VOLUME_CONTROL_MODE_SET) {
+            audioService.setStreamVolume(stream, volIndex, flag, pack/*callingPackage*/);
+        } else if (mode == VOLUME_CONTROL_MODE_ADJUST) {
+            audioService.adjustStreamVolume(stream, adjDir, flag, pack);
+        }
+    }
+
+    //--------------------------------------------
+    // Utilities
+
+    static final String LOG_V = "[v]";
+    static final String LOG_W = "[w]";
+    static final String LOG_OK = "[ok]";
+
+    static void log(String code, String msg) {
+        System.out.println(code + " " + msg);
+    }
+
+    static String streamName(int stream) {
+        try {
+            return AudioSystem.STREAM_NAMES[stream];
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return "invalid stream";
+        }
+    }
+
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 50f46f4..810d201 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -553,7 +553,7 @@
                     sessionParams.abiOverride = checkAbiArgument(nextOptionData());
                     break;
                 case "--ephemeral":
-                    sessionParams.installFlags |= PackageManager.INSTALL_EPHEMERAL;
+                    sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
                     break;
                 case "--user":
                     params.userId = UserHandle.parseUserArg(nextOptionData());
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 32b4595..b0ab235 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -20,6 +20,7 @@
 import android.app.ContentProviderHolder;
 import android.app.IActivityManager;
 import android.app.UiAutomation;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IContentProvider;
 import android.database.Cursor;
@@ -69,10 +70,12 @@
                 cursor = provider.query(null, Settings.Secure.CONTENT_URI,
                         new String[] {
                             Settings.Secure.VALUE
-                        }, "name=?",
-                        new String[] {
-                            Settings.Secure.LONG_PRESS_TIMEOUT
-                        }, null, null);
+                        },
+                        ContentResolver.createSqlQueryBundle(
+                                "name=?",
+                                new String[] { Settings.Secure.LONG_PRESS_TIMEOUT },
+                                null),
+                        null);
                 if (cursor.moveToFirst()) {
                     longPressTimeout = cursor.getInt(0);
                 }
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 0c21c4f..4707bed 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -38,8 +38,8 @@
  *
  * {@sample development/samples/ApiDemos/res/anim/object_animator.xml ObjectAnimatorResources}
  *
- * <p>When using resource files, it is possible to use {@link PropertyValuesHolder} and
- * {@link Keyframe} to create more complex animations. Using PropertyValuesHolders
+ * <p>Starting from API 23, it is possible to use {@link PropertyValuesHolder} and
+ * {@link Keyframe} in resource files to create more complex animations. Using PropertyValuesHolders
  * allows animators to animate several properties in parallel, as shown in this sample:</p>
  *
  * {@sample development/samples/ApiDemos/res/anim/object_animator_pvh.xml
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ed7e89d..f0fc8af 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -49,8 +49,8 @@
  *
  * {@sample development/samples/ApiDemos/res/anim/animator.xml ValueAnimatorResources}
  *
- * <p>It is also possible to use a combination of {@link PropertyValuesHolder} and
- * {@link Keyframe} resource tags to create a multi-step animation.
+ * <p>Starting from API 23, it is also possible to use a combination of {@link PropertyValuesHolder}
+ * and {@link Keyframe} resource tags to create a multi-step animation.
  * Note that you can specify explicit fractional values (from 0 to 1) for
  * each keyframe to determine when, in the overall duration, the animation should arrive at that
  * value. Alternatively, you can leave the fractions off and the keyframes will be equally
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index eb73e36..6dd488f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2030,6 +2030,28 @@
     }
 
     /**
+     * Requests to the system that the activity can be automatically put into picture-in-picture
+     * mode when the user leaves the activity causing it normally to be hidden.  This is a *not*
+     * a guarantee that the activity will actually be put in picture-in-picture mode, and depends
+     * on a number of factors, including whether there is already something in picture-in-picture.
+     *
+     * If {@param enterPictureInPictureOnMoveToBg} is true, then you may also call
+     * {@link #setPictureInPictureAspectRatio(float)} to specify the aspect ratio to automatically
+     * enter picture-in-picture with.
+     *
+     * @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
+     *                                     picture-in-picture
+     */
+    public void enterPictureInPictureModeOnMoveToBackground(
+            boolean enterPictureInPictureOnMoveToBg) {
+        try {
+            ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
+                    enterPictureInPictureOnMoveToBg);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Called by the system when the device configuration changes while your
      * activity is running.  Note that this will <em>only</em> be called if
      * you have selected configurations you would like to handle with the
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 84adbe5..105d2ca 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -351,17 +351,7 @@
 
     public BackStackRecord(FragmentManagerImpl manager) {
         mManager = manager;
-        FragmentHostCallback host = manager.mHost;
-        if (host != null) {
-            Context context = host.getContext();
-            if (context != null) {
-                ApplicationInfo info = context.getApplicationInfo();
-                if (info != null) {
-                    int targetSdkVersion = info.targetSdkVersion;
-                    mAllowOptimization = targetSdkVersion > Build.VERSION_CODES.N_MR1;
-                }
-            }
-        }
+        mAllowOptimization = getTargetSdk() > Build.VERSION_CODES.N_MR1;
     }
 
     public int getId() {
@@ -377,14 +367,14 @@
     }
 
     public CharSequence getBreadCrumbTitle() {
-        if (mBreadCrumbTitleRes != 0) {
+        if (mBreadCrumbTitleRes != 0 && mManager.mHost != null) {
             return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
         }
         return mBreadCrumbTitleText;
     }
 
     public CharSequence getBreadCrumbShortTitle() {
-        if (mBreadCrumbShortTitleRes != 0) {
+        if (mBreadCrumbShortTitleRes != 0 && mManager.mHost != null) {
             return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
         }
         return mBreadCrumbShortTitleText;
@@ -414,14 +404,10 @@
     }
 
     private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
-        if (mManager.mHost.getContext() != null) {
-            final int targetSdkVersion =
-                    mManager.mHost.getContext().getApplicationInfo().targetSdkVersion;
+        if (getTargetSdk() > Build.VERSION_CODES.N_MR1) {
             final Class fragmentClass = fragment.getClass();
             final int modifiers = fragmentClass.getModifiers();
-            // TODO: make the check N_MR1 or O
-            if (targetSdkVersion > Build.VERSION_CODES.N && (fragmentClass.isAnonymousClass()
-                    || !Modifier.isPublic(modifiers)
+            if ((fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                     || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)))) {
                 throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                         + " must be a public static class to be  properly recreated from"
@@ -927,4 +913,22 @@
     public boolean isEmpty() {
         return mOps.isEmpty();
     }
+
+    /**
+     * @return the target SDK of the FragmentManager's application info. If the
+     * FragmentManager has been torn down, then 0 is returned.
+     */
+    private int getTargetSdk() {
+        FragmentHostCallback host = mManager.mHost;
+        if (host != null) {
+            Context context = host.getContext();
+            if (context != null) {
+                ApplicationInfo info = context.getApplicationInfo();
+                if (info != null) {
+                    return info.targetSdkVersion;
+                }
+            }
+        }
+        return 0;
+    }
 }
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 10d584f..b569b79 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -457,7 +457,7 @@
             public void onSharedElementsReady() {
                 final View decorView = getDecor();
                 if (decorView != null) {
-                    OneShotPreDrawListener.add(decorView, () -> {
+                    OneShotPreDrawListener.add(decorView, false, () -> {
                         startTransition(() -> {
                                 startSharedElementTransition(sharedElementState);
                         });
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 21e3aee..2672798 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -124,13 +124,16 @@
     }
 
     public void resetViews() {
+        ViewGroup decorView = getDecor();
+        if (decorView != null) {
+            TransitionManager.endTransitions(decorView);
+        }
         if (mTransitioningViews != null) {
             showViews(mTransitioningViews, true);
             setTransitioningViewsVisiblity(View.VISIBLE, true);
         }
         showViews(mSharedElements, true);
         mIsHidden = true;
-        ViewGroup decorView = getDecor();
         if (!mIsReturning && decorView != null) {
             decorView.suppressLayout(false);
         }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 640ca6c..e378f4ad 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -78,10 +78,12 @@
  */
 interface IActivityManager {
     // WARNING: when these transactions are updated, check if they are any callers on the native
-    // side. If so, make sure they are using the correct transaction ids.
+    // side. If so, make sure they are using the correct transaction ids and arguments.
     // If a transaction which will also be used on the native side is being inserted, add it to
     // below block of transactions.
 
+    // Since these transactions are also called from native code, these must be kept in sync with
+    // the ones in frameworks/native/include/binder/IActivityManager.h
     // =============== Beginning of transactions used on native side as well ======================
     ParcelFileDescriptor openContentUri(in String uriString);
     // =============== End of transactions used on native side as well ============================
@@ -468,6 +470,10 @@
     boolean isInPictureInPictureMode(in IBinder token);
     void killPackageDependents(in String packageName, int userId);
     void enterPictureInPictureMode(in IBinder token);
+    void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio);
+    void enterPictureInPictureModeOnMoveToBackground(in IBinder token,
+            boolean enterPictureInPictureOnMoveToBg);
+    void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio);
     void activityRelaunched(in IBinder token);
     IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
     /**
@@ -568,14 +574,12 @@
     boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId);
     void unregisterTaskStackListener(ITaskStackListener listener);
     void moveStackToDisplay(int stackId, int displayId);
-    void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio);
-    void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio);
     boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras,
             in IBinder activityToken);
     void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback);
 
     // WARNING: when these transactions are updated, check if they are any callers on the native
-    // side. If so, make sure they are using the correct transaction ids.
+    // side. If so, make sure they are using the correct transaction ids and arguments.
     // If a transaction which will also be used on the native side is being inserted, add it
     // alongside with other transactions of this kind at the top of this file.
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/MediaRouteActionProvider.java b/core/java/android/app/MediaRouteActionProvider.java
index dffa969..85ca012 100644
--- a/core/java/android/app/MediaRouteActionProvider.java
+++ b/core/java/android/app/MediaRouteActionProvider.java
@@ -119,7 +119,6 @@
         }
 
         mButton = new MediaRouteButton(mContext);
-        mButton.setCheatSheetEnabled(true);
         mButton.setRouteTypes(mRouteTypes);
         mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
         mButton.setLayoutParams(new ViewGroup.LayoutParams(
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 70a5e15..09e95df 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -24,18 +24,13 @@
 import android.content.ContextWrapper;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteGroup;
 import android.media.MediaRouter.RouteInfo;
-import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
 import android.view.SoundEffectConstants;
 import android.view.View;
-import android.widget.Toast;
 
 public class MediaRouteButton extends View {
     private final MediaRouter mRouter;
@@ -47,7 +42,6 @@
 
     private Drawable mRemoteIndicator;
     private boolean mRemoteActive;
-    private boolean mCheatSheetEnabled;
     private boolean mIsConnecting;
 
     private int mMinWidth;
@@ -98,7 +92,6 @@
         a.recycle();
 
         setClickable(true);
-        setLongClickable(true);
 
         setRouteTypes(routeTypes);
     }
@@ -178,12 +171,10 @@
         throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
     }
 
-    /**
-     * Sets whether to enable showing a toast with the content descriptor of the
-     * button when the button is long pressed.
-     */
-    void setCheatSheetEnabled(boolean enable) {
-        mCheatSheetEnabled = enable;
+    @Override
+    public void setContentDescription(CharSequence contentDescription) {
+        super.setContentDescription(contentDescription);
+        setTooltip(contentDescription);
     }
 
     @Override
@@ -197,47 +188,6 @@
     }
 
     @Override
-    public boolean performLongClick() {
-        if (super.performLongClick()) {
-            return true;
-        }
-
-        if (!mCheatSheetEnabled) {
-            return false;
-        }
-
-        final CharSequence contentDesc = getContentDescription();
-        if (TextUtils.isEmpty(contentDesc)) {
-            // Don't show the cheat sheet if we have no description
-            return false;
-        }
-
-        final int[] screenPos = new int[2];
-        final Rect displayFrame = new Rect();
-        getLocationOnScreen(screenPos);
-        getWindowVisibleDisplayFrame(displayFrame);
-
-        final Context context = getContext();
-        final int width = getWidth();
-        final int height = getHeight();
-        final int midy = screenPos[1] + height / 2;
-        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-
-        Toast cheatSheet = Toast.makeText(context, contentDesc, Toast.LENGTH_SHORT);
-        if (midy < displayFrame.height()) {
-            // Show along the top; follow action buttons
-            cheatSheet.setGravity(Gravity.TOP | Gravity.END,
-                    screenWidth - screenPos[0] - width / 2, height);
-        } else {
-            // Show along the bottom center
-            cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
-        }
-        cheatSheet.show();
-        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        return true;
-    }
-
-    @Override
     protected int[] onCreateDrawableState(int extraSpace) {
         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
 
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index c1180e2..c5a8288 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -26,6 +26,8 @@
 import android.util.Log;
 
 import com.google.android.collect.Maps;
+
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.XmlUtils;
 
 import dalvik.system.BlockGuard;
@@ -72,6 +74,14 @@
     private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
             new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
 
+    /** Current memory state (always increasing) */
+    @GuardedBy("this")
+    private long mCurrentMemoryStateGeneration;
+
+    /** Latest memory state that was committed to disk */
+    @GuardedBy("mWritingToDiskLock")
+    private long mDiskStateGeneration;
+
     SharedPreferencesImpl(File file, int mode) {
         mFile = file;
         mBackupFile = makeBackupFile(file);
@@ -289,7 +299,7 @@
 
     // Return value from EditorImpl#commitToMemory()
     private static class MemoryCommitResult {
-        public boolean changesMade;  // any keys different?
+        public long memoryStateGeneration;
         public List<String> keysModified;  // may be null
         public Set<OnSharedPreferenceChangeListener> listeners;  // may be null
         public Map<?, ?> mapToWriteToDisk;
@@ -412,9 +422,11 @@
                 }
 
                 synchronized (this) {
+                    boolean changesMade = false;
+
                     if (mClear) {
                         if (!mMap.isEmpty()) {
-                            mcr.changesMade = true;
+                            changesMade = true;
                             mMap.clear();
                         }
                         mClear = false;
@@ -441,13 +453,19 @@
                             mMap.put(k, v);
                         }
 
-                        mcr.changesMade = true;
+                        changesMade = true;
                         if (hasListeners) {
                             mcr.keysModified.add(k);
                         }
                     }
 
                     mModified.clear();
+
+                    if (changesMade) {
+                        mCurrentMemoryStateGeneration++;
+                    }
+
+                    mcr.memoryStateGeneration = mCurrentMemoryStateGeneration;
                 }
             }
             return mcr;
@@ -509,10 +527,12 @@
      */
     private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                   final Runnable postWriteRunnable) {
+        final boolean isFromSyncCommit = (postWriteRunnable == null);
+
         final Runnable writeToDiskRunnable = new Runnable() {
                 public void run() {
                     synchronized (mWritingToDiskLock) {
-                        writeToFile(mcr);
+                        writeToFile(mcr, isFromSyncCommit);
                     }
                     synchronized (SharedPreferencesImpl.this) {
                         mDiskWritesInFlight--;
@@ -523,8 +543,6 @@
                 }
             };
 
-        final boolean isFromSyncCommit = (postWriteRunnable == null);
-
         // Typical #commit() path with fewer allocations, doing a write on
         // the current thread.
         if (isFromSyncCommit) {
@@ -538,6 +556,10 @@
             }
         }
 
+        if (DEBUG) {
+            Log.d(TAG, "added " + mcr.memoryStateGeneration + " -> " + mFile.getName());
+        }
+
         QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
     }
 
@@ -565,17 +587,34 @@
     }
 
     // Note: must hold mWritingToDiskLock
-    private void writeToFile(MemoryCommitResult mcr) {
+    private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
         // Rename the current file so it may be used as a backup during the next read
         if (mFile.exists()) {
-            if (!mcr.changesMade) {
-                // If the file already exists, but no changes were
-                // made to the underlying map, it's wasteful to
-                // re-write the file.  Return as if we wrote it
-                // out.
+            boolean needsWrite = false;
+
+            if (isFromSyncCommit) {
+                // Only need to write if the disk state is older than this commit
+                if (mDiskStateGeneration < mcr.memoryStateGeneration) {
+                    needsWrite = true;
+                }
+            } else {
+                synchronized (this) {
+                    // No need to persist intermediate states. Just wait for the latest state to be
+                    // persisted.
+                    if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
+                        needsWrite = true;
+                    }
+                }
+            }
+
+            if (!needsWrite) {
+                if (DEBUG) {
+                    Log.d(TAG, "skipped " + mcr.memoryStateGeneration + " -> " + mFile.getName());
+                }
                 mcr.setDiskWriteResult(true);
                 return;
             }
+
             if (!mBackupFile.exists()) {
                 if (!mFile.renameTo(mBackupFile)) {
                     Log.e(TAG, "Couldn't rename file " + mFile
@@ -599,6 +638,11 @@
             }
             XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
             FileUtils.sync(str);
+
+            if (DEBUG) {
+                Log.d(TAG, "wrote " + mcr.memoryStateGeneration + " -> " + mFile.getName());
+            }
+
             str.close();
             ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
             try {
@@ -612,7 +656,11 @@
             }
             // Writing was successful, delete the backup file if there is one.
             mBackupFile.delete();
+
+            mDiskStateGeneration = mcr.memoryStateGeneration;
+
             mcr.setDiskWriteResult(true);
+
             return;
         } catch (XmlPullParserException e) {
             Log.w(TAG, "writeToFile: Got exception:", e);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ce10bad..39f415e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6939,11 +6939,13 @@
     /**
      * Return whether network logging is enabled by a device owner.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
+     * be {@code null} if the caller has MANAGE_USERS permission.
      * @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
-     * @throws {@link SecurityException} if {@code admin} is not a device owner.
+     * @throws {@link SecurityException} if {@code admin} is not a device owner and caller has
+     * no MANAGE_USERS permission
      */
-    public boolean isNetworkLoggingEnabled(@NonNull ComponentName admin) {
+    public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
         throwIfParentInstance("isNetworkLoggingEnabled");
         try {
             return mService.isNetworkLoggingEnabled(admin);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index f369409..cda98e5 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -97,6 +97,7 @@
  * developer guide.</p>
  */
 public abstract class ContentProvider implements ComponentCallbacks2 {
+
     private static final String TAG = "ContentProvider";
 
     /*
@@ -118,7 +119,7 @@
     private boolean mNoPerms;
     private boolean mSingleUser;
 
-    private final ThreadLocal<String> mCallingPackage = new ThreadLocal<String>();
+    private final ThreadLocal<String> mCallingPackage = new ThreadLocal<>();
 
     private Transport mTransport = new Transport();
 
@@ -205,9 +206,8 @@
         }
 
         @Override
-        public Cursor query(String callingPkg, Uri uri, String[] projection,
-                String selection, String[] selectionArgs, String sortOrder,
-                ICancellationSignal cancellationSignal) {
+        public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
+                @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
             validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -225,9 +225,9 @@
                 // However, the caller may be expecting to access them my index. Hence,
                 // we have to execute the query as if allowed to get a cursor with the
                 // columns. We then use the column names to return an empty cursor.
-                Cursor cursor = ContentProvider.this.query(uri, projection, selection,
-                        selectionArgs, sortOrder, CancellationSignal.fromTransport(
-                                cancellationSignal));
+                Cursor cursor = ContentProvider.this.query(
+                        uri, projection, queryArgs,
+                        CancellationSignal.fromTransport(cancellationSignal));
                 if (cursor == null) {
                     return null;
                 }
@@ -238,7 +238,7 @@
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.query(
-                        uri, projection, selection, selectionArgs, sortOrder,
+                        uri, projection, queryArgs,
                         CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingPackage(original);
@@ -893,6 +893,7 @@
      * (Content providers do not usually care about things like screen
      * orientation, but may want to know about locale changes.)
      */
+    @Override
     public void onConfigurationChanged(Configuration newConfig) {
     }
 
@@ -904,9 +905,11 @@
      * <p>The default content provider implementation does nothing.
      * Subclasses may override this method to take appropriate action.
      */
+    @Override
     public void onLowMemory() {
     }
 
+    @Override
     public void onTrimMemory(int level) {
     }
 
@@ -1039,6 +1042,45 @@
     }
 
     /**
+     * Implement this to handle query requests where the arguments are packed into a {@link Bundle}.
+     * Arguments may include traditional SQL style query arguments. When present these
+     * should be handled  according to the contract established in
+     * {@link #query(Uri, String[], String, String[], String, CancellationSignal).
+     *
+     * <p>Traditional SQL arguments can be found in the bundle using the following keys:
+     * <li>{@link ContentResolver#QUERY_ARG_SELECTION}
+     * <li>{@link ContentResolver#QUERY_ARG_SELECTION_ARGS}
+     * <li>{@link ContentResolver#QUERY_ARG_SORT_ORDER}
+     *
+     * @see #query(Uri, String[], String, String[], String, CancellationSignal) for
+     *     implementation details.
+     *
+     * @param uri The URI to query. This will be the full URI sent by the client.
+     *            TODO: Me wonders about this use case, and how we adapt it.
+     *            If the client is requesting a specific record, the URI will end
+     *            in a record number that the implementation should parse and add
+     *            to a WHERE or HAVING clause, specifying that _id value.
+     * @param projection The list of columns to put into the cursor.
+     *            If {@code null} provide a default set of columns.
+     * @param queryArgs A Bundle containing all additional information necessary for the query.
+     *            Values in the Bundle may include SQL style arguments.
+     * @param cancellationSignal A signal to cancel the operation in progress,
+     *            or {@code null}.
+     * @return a Cursor or {@code null}.
+     */
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+        queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;
+        return query(
+                uri,
+                projection,
+                queryArgs.getString(ContentResolver.QUERY_ARG_SELECTION),
+                queryArgs.getStringArray(ContentResolver.QUERY_ARG_SELECTION_ARGS),
+                queryArgs.getString(ContentResolver.QUERY_ARG_SORT_ORDER),
+                cancellationSignal);
+    }
+
+    /**
      * Implement this to handle requests for the MIME type of the data at the
      * given URI.  The returned MIME type should start with
      * <code>vnd.android.cursor.item</code> for a single record,
@@ -1412,7 +1454,7 @@
      * no file associated with the given URI or the mode is invalid.
      * @throws SecurityException Throws SecurityException if the caller does
      * not have permission to access the file.
-     * 
+     *
      * @see #openFile(Uri, String)
      * @see #openFileHelper(Uri, String)
      * @see #getType(android.net.Uri)
@@ -1851,7 +1893,7 @@
     /**
      * Implement this to shut down the ContentProvider instance. You can then
      * invoke this method in unit tests.
-     * 
+     *
      * <p>
      * Android normally handles ContentProvider startup and shutdown
      * automatically. You do not need to start up or shut down a
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index fd6cddb..732666f 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -128,11 +128,20 @@
     }
 
     /** See {@link ContentProvider#query ContentProvider.query} */
-    public @Nullable Cursor query(@NonNull Uri url, @Nullable String[] projection,
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
             @Nullable String selection, @Nullable String[] selectionArgs,
             @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)
                     throws RemoteException {
-        Preconditions.checkNotNull(url, "url");
+        Bundle queryArgs =
+                ContentResolver.createSqlQueryBundle(selection, selectionArgs, sortOrder);
+        return query(uri, projection, queryArgs, cancellationSignal);
+    }
+
+    /** See {@link ContentProvider#query ContentProvider.query} */
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
+                    throws RemoteException {
+        Preconditions.checkNotNull(uri, "url");
 
         beforeRemote();
         try {
@@ -142,8 +151,8 @@
                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
-            final Cursor cursor = mContentProvider.query(mPackageName, url, projection, selection,
-                    selectionArgs, sortOrder, remoteCancellationSignal);
+            final Cursor cursor = mContentProvider.query(
+                    mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
             if (cursor == null) {
                 return null;
             }
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index eadc013..d428a3a 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.Nullable;
 import android.content.res.AssetFileDescriptor;
 import android.database.BulkCursorDescriptor;
 import android.database.BulkCursorToCursorAdaptor;
@@ -92,25 +93,13 @@
                         }
                     }
 
-                    // String selection, String[] selectionArgs...
-                    String selection = data.readString();
-                    num = data.readInt();
-                    String[] selectionArgs = null;
-                    if (num > 0) {
-                        selectionArgs = new String[num];
-                        for (int i = 0; i < num; i++) {
-                            selectionArgs[i] = data.readString();
-                        }
-                    }
-
-                    String sortOrder = data.readString();
+                    Bundle queryArgs = data.readBundle();
                     IContentObserver observer = IContentObserver.Stub.asInterface(
                             data.readStrongBinder());
                     ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                             data.readStrongBinder());
 
-                    Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
-                            sortOrder, cancellationSignal);
+                    Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);
                     if (cursor != null) {
                         CursorToBulkCursorAdaptor adaptor = null;
 
@@ -185,7 +174,7 @@
                     String callingPkg = data.readString();
                     final int numOperations = data.readInt();
                     final ArrayList<ContentProviderOperation> operations =
-                            new ArrayList<ContentProviderOperation>(numOperations);
+                            new ArrayList<>(numOperations);
                     for (int i = 0; i < numOperations; i++) {
                         operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
                     }
@@ -378,6 +367,7 @@
         return super.onTransact(code, data, reply, flags);
     }
 
+    @Override
     public IBinder asBinder()
     {
         return this;
@@ -392,14 +382,16 @@
         mRemote = remote;
     }
 
+    @Override
     public IBinder asBinder()
     {
         return mRemote;
     }
 
-    public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
-                    throws RemoteException {
+    @Override
+    public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
+            throws RemoteException {
         BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -416,19 +408,10 @@
             for (int i = 0; i < length; i++) {
                 data.writeString(projection[i]);
             }
-            data.writeString(selection);
-            if (selectionArgs != null) {
-                length = selectionArgs.length;
-            } else {
-                length = 0;
-            }
-            data.writeInt(length);
-            for (int i = 0; i < length; i++) {
-                data.writeString(selectionArgs[i]);
-            }
-            data.writeString(sortOrder);
+            data.writeBundle(queryArgs);
             data.writeStrongBinder(adaptor.getObserver().asBinder());
-            data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);
+            data.writeStrongBinder(
+                    cancellationSignal != null ? cancellationSignal.asBinder() : null);
 
             mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
 
@@ -455,6 +438,7 @@
         }
     }
 
+    @Override
     public String getType(Uri url) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -475,6 +459,7 @@
         }
     }
 
+    @Override
     public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -497,6 +482,7 @@
         }
     }
 
+    @Override
     public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -518,7 +504,8 @@
         }
     }
 
-    public ContentProviderResult[] applyBatch(String callingPkg, 
+    @Override
+    public ContentProviderResult[] applyBatch(String callingPkg,
             ArrayList<ContentProviderOperation> operations)
                     throws RemoteException, OperationApplicationException {
         Parcel data = Parcel.obtain();
@@ -542,6 +529,7 @@
         }
     }
 
+    @Override
     public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -565,6 +553,7 @@
         }
     }
 
+    @Override
     public int update(String callingPkg, Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -644,6 +633,7 @@
         }
     }
 
+    @Override
     public Bundle call(String callingPkg, String method, String request, Bundle args)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -667,6 +657,7 @@
         }
     }
 
+    @Override
     public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -715,6 +706,7 @@
         }
     }
 
+    @Override
     public ICancellationSignal createCancellationSignal() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -734,6 +726,7 @@
         }
     }
 
+    @Override
     public Uri canonicalize(String callingPkg, Uri url) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -755,6 +748,7 @@
         }
     }
 
+    @Override
     public Uri uncanonicalize(String callingPkg, Uri url) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -775,6 +769,7 @@
         }
     }
 
+    @Override
     public boolean refresh(String callingPkg, Uri url, Bundle args, ICancellationSignal signal)
             throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 54dcd0a..0fe5ce9 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -203,6 +203,26 @@
     public static final String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
 
     /**
+     * Key for an SQL style selection string that may be present in the query Bundle argument
+     * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
+     * when called by a legacy client.
+     */
+    public static final String QUERY_ARG_SELECTION = "android:query-selection";
+
+    /**
+     * Key for sql selection string arguments list.
+     * @see #QUERY_ARG_SELECTION
+     */
+    public static final String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
+
+    /**
+     * Key for an SQL style sort string that may be present in the query Bundle argument
+     * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
+     * when called by a legacy client.
+     */
+    public static final String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
+
+    /**
      * This is the Android platform's base MIME type for a content: URI
      * containing a Cursor of a single item.  Applications should use this
      * as the base type along with their own sub-type of their content: URIs
@@ -517,10 +537,37 @@
      * @return A Cursor object, which is positioned before the first entry, or null
      * @see Cursor
      */
-    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
+    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
             @Nullable String[] projection, @Nullable String selection,
             @Nullable String[] selectionArgs, @Nullable String sortOrder,
             @Nullable CancellationSignal cancellationSignal) {
+        Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
+        return query(uri, projection, queryArgs, cancellationSignal);
+    }
+
+    /**
+     * Query the given URI, returning a {@link Cursor} over the result set
+     * with support for cancellation.
+     *
+     * <p>For best performance, the caller should follow these guidelines:
+     *
+     * <li>Provide an explicit projection, to prevent reading data from storage
+     * that aren't going to be used.
+     *
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param projection A list of which columns to return. Passing null will
+     *         return all columns, which is inefficient.
+     * @param queryArgs A Bundle containing any arguments to the query.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+     * when the query is executed.
+     * @return A Cursor object, which is positioned before the first entry, or null
+     * @see Cursor
+     */
+    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
+            @Nullable String[] projection, @Nullable Bundle queryArgs,
+            @Nullable CancellationSignal cancellationSignal) {
         Preconditions.checkNotNull(uri, "uri");
         IContentProvider unstableProvider = acquireUnstableProvider(uri);
         if (unstableProvider == null) {
@@ -539,7 +586,7 @@
             }
             try {
                 qCursor = unstableProvider.query(mPackageName, uri, projection,
-                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
+                        queryArgs, remoteCancellationSignal);
             } catch (DeadObjectException e) {
                 // The remote process has died...  but we only hold an unstable
                 // reference though, so we might recover!!!  Let's try!!!!
@@ -549,8 +596,8 @@
                 if (stableProvider == null) {
                     return null;
                 }
-                qCursor = stableProvider.query(mPackageName, uri, projection,
-                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
+                qCursor = stableProvider.query(
+                        mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
             }
             if (qCursor == null) {
                 return null;
@@ -559,7 +606,7 @@
             // Force query execution.  Might fail and throw a runtime exception here.
             qCursor.getCount();
             long durationMillis = SystemClock.uptimeMillis() - startTime;
-            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
+            maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
 
             // Wrap the cursor object into CursorWrapperInner object.
             final IContentProvider provider = (stableProvider != null) ? stableProvider
@@ -2541,6 +2588,7 @@
         }
         try {
             ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
+                @Override
                 public void onStatusChanged(int which) throws RemoteException {
                     callback.onStatusChanged(which);
                 }
@@ -2602,9 +2650,8 @@
         return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1;
     }
 
-    private void maybeLogQueryToEventLog(long durationMillis,
-                                         Uri uri, String[] projection,
-                                         String selection, String sortOrder) {
+    private void maybeLogQueryToEventLog(
+            long durationMillis, Uri uri, String[] projection, @Nullable Bundle queryArgs) {
         if (!ENABLE_CONTENT_SAMPLE) return;
         int samplePercent = samplePercentForDuration(durationMillis);
         if (samplePercent < 100) {
@@ -2615,6 +2662,9 @@
             }
         }
 
+        // Ensure a non-null bundle.
+        queryArgs = (queryArgs != null) ? queryArgs : Bundle.EMPTY;
+
         StringBuilder projectionBuffer = new StringBuilder(100);
         if (projection != null) {
             for (int i = 0; i < projection.length; ++i) {
@@ -2636,8 +2686,8 @@
             EventLogTags.CONTENT_QUERY_SAMPLE,
             uri.toString(),
             projectionBuffer.toString(),
-            selection != null ? selection : "",
-            sortOrder != null ? sortOrder : "",
+            queryArgs.getString(QUERY_ARG_SELECTION, ""),
+            queryArgs.getString(QUERY_ARG_SORT_ORDER, ""),
             durationMillis,
             blockingPackage != null ? blockingPackage : "",
             samplePercent);
@@ -2751,4 +2801,29 @@
     public Drawable getTypeDrawable(String mimeType) {
         return MimeIconUtils.loadMimeIcon(mContext, mimeType);
     }
+
+    /**
+     * @hide
+     */
+    public static @Nullable Bundle createSqlQueryBundle(
+            @Nullable String selection,
+            @Nullable String[] selectionArgs,
+            @Nullable String sortOrder) {
+
+        if (selection == null && selectionArgs == null && sortOrder == null) {
+            return null;
+        }
+
+        Bundle queryArgs = new Bundle();
+        if (selection != null) {
+            queryArgs.putString(QUERY_ARG_SELECTION, selection);
+        }
+        if (selectionArgs != null) {
+            queryArgs.putStringArray(QUERY_ARG_SELECTION_ARGS, selectionArgs);
+        }
+        if (sortOrder != null) {
+            queryArgs.putString(QUERY_ARG_SORT_ORDER, sortOrder);
+        }
+        return queryArgs;
+    }
 }
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index ee8a22f..66087fb 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -35,9 +35,9 @@
  * @hide
  */
 public interface IContentProvider extends IInterface {
-    public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
-                    throws RemoteException;
+    public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
+            throws RemoteException;
     public String getType(Uri url) throws RemoteException;
     public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
             throws RemoteException;
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e2ebd46..5d90acc 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -182,12 +182,30 @@
      */
     public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
     /**
-     * Activity is does not support resizing, but we are forcing it to be resizeable. Only affects
+     * Activity does not support resizing, but we are forcing it to be resizeable. Only affects
      * certain pre-N apps where we force them to be resizeable.
      * @hide
      */
     public static final int RESIZE_MODE_FORCE_RESIZEABLE = 4;
     /**
+     * Activity does not support resizing, but we are forcing it to be resizeable as long
+     * as the size remains landscape.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY = 5;
+    /**
+     * Activity does not support resizing, but we are forcing it to be resizeable as long
+     * as the size remains portrait.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY = 6;
+    /**
+     * Activity does not support resizing, but we are forcing it to be resizeable as long
+     * as the bounds remain in the same orientation as they are.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION = 7;
+    /**
      * Value indicating if the resizing mode the activity supports.
      * See {@link android.R.attr#resizeableActivity}.
      * @hide
@@ -859,26 +877,51 @@
      * @hide
      */
     boolean isFixedOrientation() {
-        return screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
-                || screenOrientation == SCREEN_ORIENTATION_PORTRAIT
-                || screenOrientation == SCREEN_ORIENTATION_SENSOR_LANDSCAPE
-                || screenOrientation == SCREEN_ORIENTATION_SENSOR_PORTRAIT
-                || screenOrientation == SCREEN_ORIENTATION_REVERSE_LANDSCAPE
-                || screenOrientation == SCREEN_ORIENTATION_REVERSE_PORTRAIT
-                || screenOrientation == SCREEN_ORIENTATION_USER_LANDSCAPE
-                || screenOrientation == SCREEN_ORIENTATION_USER_PORTRAIT
+        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                 || screenOrientation == SCREEN_ORIENTATION_LOCKED;
     }
 
+    /**
+     * Returns true if the activity's orientation is fixed to landscape.
+     * @hide
+     */
+    boolean isFixedOrientationLandscape() {
+        return screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
+                || screenOrientation == SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+                || screenOrientation == SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+                || screenOrientation == SCREEN_ORIENTATION_USER_LANDSCAPE;
+    }
+
+    /**
+     * Returns true if the activity's orientation is fixed to portrait.
+     * @hide
+     */
+    boolean isFixedOrientationPortrait() {
+        return screenOrientation == SCREEN_ORIENTATION_PORTRAIT
+                || screenOrientation == SCREEN_ORIENTATION_SENSOR_PORTRAIT
+                || screenOrientation == SCREEN_ORIENTATION_REVERSE_PORTRAIT
+                || screenOrientation == SCREEN_ORIENTATION_USER_PORTRAIT;
+    }
+
     /** @hide */
     public static boolean isResizeableMode(int mode) {
         return mode == RESIZE_MODE_RESIZEABLE
                 || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
                 || mode == RESIZE_MODE_FORCE_RESIZEABLE
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION
                 || mode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
     }
 
     /** @hide */
+    public static boolean isPreserveOrientationMode(int mode) {
+        return mode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
+                || mode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+    }
+
+    /** @hide */
     public static String resizeModeToString(int mode) {
         switch (mode) {
             case RESIZE_MODE_UNRESIZEABLE:
@@ -891,6 +934,12 @@
                 return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
             case RESIZE_MODE_FORCE_RESIZEABLE:
                 return "RESIZE_MODE_FORCE_RESIZEABLE";
+            case RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY:
+                return "RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY";
+            case RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY:
+                return "RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY";
+            case RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION:
+                return "RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION";
             default:
                 return "unknown=" + mode;
         }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index ed8143e..646bd3c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1067,6 +1067,16 @@
         }
 
         /** {@hide} */
+        @SystemApi
+        public void setInstallAsInstantApp(boolean isInstantApp) {
+            if (isInstantApp) {
+                installFlags |= PackageManager.INSTALL_EPHEMERAL;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_EPHEMERAL;
+            }
+        }
+
+        /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
             pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3f052d38..ecdd02a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -436,9 +436,10 @@
 
     /**
      * Allows querying of packages installed for any user, not just the specific one. This flag
-     * is only meant for use by apps that have INTERACT_ACROSS_USERS_FULL permission.
+     * is only meant for use by apps that have INTERACT_ACROSS_USERS permission.
      * @hide
      */
+    @SystemApi
     public static final int MATCH_ANY_USER = 0x00400000;
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f5eb4bd..9b2dd68 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -88,6 +88,9 @@
 import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
 import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
@@ -3896,8 +3899,15 @@
 
         // resize preference isn't set and target sdk version doesn't support resizing apps by
         // default. For the app to be resizeable if it isn't fixed orientation or immersive.
-        aInfo.resizeMode = (aInfo.isFixedOrientation() || (aInfo.flags & FLAG_IMMERSIVE) != 0)
-                ? RESIZE_MODE_UNRESIZEABLE : RESIZE_MODE_FORCE_RESIZEABLE;
+        if (aInfo.isFixedOrientationPortrait()) {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+        } else if (aInfo.isFixedOrientationLandscape()) {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+        } else if (aInfo.isFixedOrientation()) {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+        } else {
+            aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        }
     }
 
     private void parseLayout(Resources res, AttributeSet attrs, Activity a) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 3e79118..1a05904 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -348,9 +348,7 @@
 
                     Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
                     if (sizes == null) {
-                        // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
-                        if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
-                            surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
+                        if (surfaceType == ImageFormat.PRIVATE) {
 
                             // YUV_420_888 is always present in LEGACY for all
                             // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
@@ -649,7 +647,16 @@
      */
     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
         checkNotNull(surface);
-        return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
+        int surfaceType = nativeDetectSurfaceType(surface);
+
+        // TODO: remove this override since the default format should be
+        // ImageFormat.PRIVATE. b/9487482
+        if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
+                surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
+            surfaceType = ImageFormat.PRIVATE;
+        }
+
+        return LegacyExceptionUtils.throwOnError(surfaceType);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index dfa19b0..dbe1394 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -408,12 +408,6 @@
         // See if consumer is flexible.
         boolean isFlexible = SurfaceUtils.isFlexibleConsumer(surface);
 
-        // Override RGB formats to IMPLEMENTATION_DEFINED, b/9487482
-        if ((surfaceFormat >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
-                        surfaceFormat <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
-            surfaceFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
-        }
-
         StreamConfiguration[] configs =
                 surfaceDataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations;
         for (StreamConfiguration config : configs) {
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index 4b958df..e1e1c4f 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -118,15 +118,7 @@
      * @param surface The high speed output surface to be checked.
      */
     private static void checkHighSpeedSurfaceFormat(Surface surface) {
-        // TODO: remove this override since the default format should be
-        // ImageFormat.PRIVATE. b/9487482
-        final int HAL_FORMAT_RGB_START = 1; // HAL_PIXEL_FORMAT_RGBA_8888 from graphics.h
-        final int HAL_FORMAT_RGB_END = 5; // HAL_PIXEL_FORMAT_BGRA_8888 from graphics.h
         int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
-        if (surfaceFormat >= HAL_FORMAT_RGB_START &&
-                surfaceFormat <= HAL_FORMAT_RGB_END) {
-            surfaceFormat = ImageFormat.PRIVATE;
-        }
 
         if (surfaceFormat != ImageFormat.PRIVATE) {
             throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not"
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 59cbf6e..24f4504 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -56,15 +56,26 @@
     void disableScoring();
 
     /**
-     * Register a network subsystem for scoring.
+     * Register a cache to receive scoring updates.
+     *
+     * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}
+     * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores
+     * @param filterType the {@link CacheUpdateFilter} to apply
+     * @throws SecurityException if the caller is not the system
+     * @throws IllegalArgumentException if a score cache is already registed for this type
+     * @hide
+     */
+    void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache, int filterType);
+
+    /**
+     * Unregister a cache to receive scoring updates.
      *
      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
      * @throws SecurityException if the caller is not the system.
-     * @throws IllegalArgumentException if a score cache is already registed for this type.
      * @hide
      */
-    void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache);
+    void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache);
 
     /**
      * Request a recommendation for the best network to connect to
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index a2d2b58..865b8dd 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -28,6 +29,9 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.UserHandle;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Class that manages communication between network subsystems and a network scorer.
  *
@@ -131,6 +135,29 @@
      */
     public static final String EXTRA_NEW_SCORER = "newScorer";
 
+    /** @hide */
+    @IntDef({CACHE_FILTER_NONE, CACHE_FILTER_CURRENT_NETWORK, CACHE_FILTER_SCAN_RESULTS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CacheUpdateFilter {}
+
+    /**
+     * Do not filter updates sent to the cache.
+     * @hide
+     */
+    public static final int CACHE_FILTER_NONE = 0;
+
+    /**
+     * Only send cache updates when the network matches the connected network.
+     * @hide
+     */
+    public static final int CACHE_FILTER_CURRENT_NETWORK = 1;
+
+    /**
+     * Only send cache updates when the network is part of the current scan result set.
+     * @hide
+     */
+    public static final int CACHE_FILTER_SCAN_RESULTS = 2;
+
     private final Context mContext;
     private final INetworkScoreService mService;
 
@@ -268,11 +295,47 @@
      * @throws SecurityException if the caller does not hold the
      *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
      * @throws IllegalArgumentException if a score cache is already registered for this type.
+     * @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE.
      * @hide
      */
+    @Deprecated // migrate to registerNetworkScoreCache(int, INetworkScoreCache, int)
     public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
+        registerNetworkScoreCache(networkType, scoreCache, CACHE_FILTER_NONE);
+    }
+
+    /**
+     * Register a network score cache.
+     *
+     * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}
+     * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores
+     * @param filterType the {@link CacheUpdateFilter} to apply
+     * @throws SecurityException if the caller does not hold the
+     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     * @throws IllegalArgumentException if a score cache is already registered for this type.
+     * @hide
+     */
+    public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache,
+            @CacheUpdateFilter int filterType) {
         try {
-            mService.registerNetworkScoreCache(networkType, scoreCache);
+            mService.registerNetworkScoreCache(networkType, scoreCache, filterType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister a network score cache.
+     *
+     * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
+     * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
+     * @throws SecurityException if the caller does not hold the
+     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
+     * @throws IllegalArgumentException if a score cache is already registered for this type.
+     * @hide
+     */
+    public void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
+        try {
+            mService.unregisterNetworkScoreCache(networkType, scoreCache);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 93ac7f6..ff8b9dd 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -108,6 +108,8 @@
             RawData.COLUMN_INTENT_TARGET_CLASS,     // 11
             RawData.COLUMN_KEY,                     // 12
             RawData.COLUMN_USER_ID,                 // 13
+            RawData.PAYLOAD_TYPE,                   // 14
+            RawData.PAYLOAD                         // 15
     };
 
     /**
@@ -127,6 +129,14 @@
     public static final int COLUMN_INDEX_RAW_INTENT_TARGET_CLASS = 11;
     public static final int COLUMN_INDEX_RAW_KEY = 12;
     public static final int COLUMN_INDEX_RAW_USER_ID = 13;
+    /**
+     * @hide
+     */
+    public static final int COLUMN_INDEX_RAW_PAYLOAD_TYPE = 14;
+    /**
+     * @hide
+     */
+    public static final int COLUMN_INDEX_RAW_PAYLOAD = 15;
 
     /**
      * Indexable raw data columns.
@@ -213,6 +223,18 @@
          * UserId associated with the raw data.
          */
         public static final String COLUMN_USER_ID = "user_id";
+
+        /**
+         * Identifier for the Payload object type.
+         * @hide
+         */
+        public static final String PAYLOAD_TYPE = "payload_type";
+
+        /**
+         * Generic payload for improving Search result expressiveness.
+         * @hide
+         */
+        public static final String PAYLOAD = "payload";
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 686938f..37222ad 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -44,6 +44,7 @@
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
+import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.DropBoxManager;
 import android.os.IBinder;
@@ -52,7 +53,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.Build.VERSION_CODES;
 import android.speech.tts.TextToSpeech;
 import android.text.TextUtils;
 import android.util.AndroidException;
@@ -1580,12 +1580,13 @@
 
         private final Uri mUri;
 
-        private static final String[] SELECT_VALUE =
-            new String[] { Settings.NameValueTable.VALUE };
+        private static final String[] SELECT_VALUE_PROJECTION = new String[] {
+                Settings.NameValueTable.VALUE
+        };
         private static final String NAME_EQ_PLACEHOLDER = "name=?";
 
         // Must synchronize on 'this' to access mValues and mValuesVersion.
-        private final HashMap<String, String> mValues = new HashMap<String, String>();
+        private final HashMap<String, String> mValues = new HashMap<>();
 
         // Initially null; set lazily and held forever.  Synchronized on 'this'.
         private IContentProvider mContentProvider = null;
@@ -1738,8 +1739,9 @@
 
             Cursor c = null;
             try {
-                c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
-                             new String[]{name}, null, null);
+                Bundle queryArgs = ContentResolver.createSqlQueryBundle(
+                        NAME_EQ_PLACEHOLDER, new String[]{name}, null);
+                c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, null);
                 if (c == null) {
                     Log.w(TAG, "Can't get key " + name + " from " + mUri);
                     return null;
@@ -1807,7 +1809,7 @@
 
         private static final HashSet<String> MOVED_TO_SECURE;
         static {
-            MOVED_TO_SECURE = new HashSet<String>(30);
+            MOVED_TO_SECURE = new HashSet<>(30);
             MOVED_TO_SECURE.add(Secure.ANDROID_ID);
             MOVED_TO_SECURE.add(Secure.HTTP_PROXY);
             MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED);
@@ -1844,8 +1846,8 @@
         private static final HashSet<String> MOVED_TO_GLOBAL;
         private static final HashSet<String> MOVED_TO_SECURE_THEN_GLOBAL;
         static {
-            MOVED_TO_GLOBAL = new HashSet<String>();
-            MOVED_TO_SECURE_THEN_GLOBAL = new HashSet<String>();
+            MOVED_TO_GLOBAL = new HashSet<>();
+            MOVED_TO_SECURE_THEN_GLOBAL = new HashSet<>();
 
             // these were originally in system but migrated to secure in the past,
             // so are duplicated in the Secure.* namespace
@@ -4163,12 +4165,12 @@
         private static final HashSet<String> MOVED_TO_LOCK_SETTINGS;
         private static final HashSet<String> MOVED_TO_GLOBAL;
         static {
-            MOVED_TO_LOCK_SETTINGS = new HashSet<String>(3);
+            MOVED_TO_LOCK_SETTINGS = new HashSet<>(3);
             MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED);
             MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE);
             MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
 
-            MOVED_TO_GLOBAL = new HashSet<String>();
+            MOVED_TO_GLOBAL = new HashSet<>();
             MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.ASSISTED_GPS_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.BLUETOOTH_ON);
@@ -5203,6 +5205,7 @@
          * @hide
          * @deprecated
          */
+        @Deprecated
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE =
                 "accessibility_display_magnification_auto_update";
 
@@ -6457,7 +6460,7 @@
          * @hide
          */
         public static final String DOWNLOADS_BACKUP_ENABLED = "downloads_backup_enabled";
-        
+
         /**
          * Whether Downloads folder backup should only occur if the device is using a metered
          * network.
@@ -7757,6 +7760,8 @@
 
         /**
          * Value to specify if Wi-Fi Wakeup feature is enabled.
+         *
+         * Type: int (0 for false, 1 for true)
          * @hide
          */
         @SystemApi
@@ -7765,6 +7770,8 @@
         /**
          * Value to specify if network recommendations from
          * {@link com.android.server.NetworkScoreService} are enabled.
+         *
+         * Type: int (0 for false, 1 for true)
          * @hide
          */
         @SystemApi
@@ -9135,7 +9142,7 @@
         // Certain settings have been moved from global to the per-user secure namespace
         private static final HashSet<String> MOVED_TO_SECURE;
         static {
-            MOVED_TO_SECURE = new HashSet<String>(1);
+            MOVED_TO_SECURE = new HashSet<>(1);
             MOVED_TO_SECURE.add(Settings.Global.INSTALL_NON_MARKET_APPS);
         }
 
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 4b272e9..7af93c2 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,8 +15,7 @@
  */
 package android.service.notification;
 
-import android.annotation.SystemApi;
-import android.net.Uri;
+import android.app.NotificationChannel;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,33 +26,39 @@
 public final class Adjustment implements Parcelable {
     private final String mPackage;
     private final String mKey;
-    private final int mImportance;
     private final CharSequence mExplanation;
-    private final Uri mReference;
     private final Bundle mSignals;
     private final int mUser;
 
     /**
+     * Data type: {@code String}. See {@link NotificationChannel#getId()}.
+     */
+    public static final String KEY_CHANNEL_ID = "key_channel_id";
+    /**
+     * Data type: ArrayList of {@code String}, where each is a representation of a
+     * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+     * See {@link android.app.Notification.Builder#addPerson(String)}.
+     */
+    public static final String KEY_PEOPLE = "key_people";
+    /**
+     * Parcelable {@code ArrayList} of {@link SnoozeCriterion}.
+     */
+    public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
      * @param key The notification key.
-     * @param importance The recommended importance of the notification.
-     * @param signals A bundle of signals that should inform notification grouping and ordering.
+     * @param signals A bundle of signals that should inform notification display, ordering, and
+     *                interruptiveness.
      * @param explanation A human-readable justification for the adjustment.
-     * @param reference A reference to an external object that augments the
-     *                  explanation, such as a
-     *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
-     *                  or null.
      */
-    public Adjustment(String pkg, String key, int importance, Bundle signals,
-            CharSequence explanation, Uri reference, int user) {
+    public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) {
         mPackage = pkg;
         mKey = key;
-        mImportance = importance;
         mSignals = signals;
         mExplanation = explanation;
-        mReference = reference;
         mUser = user;
     }
 
@@ -68,13 +73,11 @@
         } else {
             mKey = null;
         }
-        mImportance = in.readInt();
         if (in.readInt() == 1) {
             mExplanation = in.readCharSequence();
         } else {
             mExplanation = null;
         }
-        mReference = in.readParcelable(Uri.class.getClassLoader());
         mSignals = in.readBundle();
         mUser = in.readInt();
     }
@@ -99,18 +102,10 @@
         return mKey;
     }
 
-    public int getImportance() {
-        return mImportance;
-    }
-
     public CharSequence getExplanation() {
         return mExplanation;
     }
 
-    public Uri getReference() {
-        return mReference;
-    }
-
     public Bundle getSignals() {
         return mSignals;
     }
@@ -138,14 +133,12 @@
         } else {
             dest.writeInt(0);
         }
-        dest.writeInt(mImportance);
         if (mExplanation != null) {
             dest.writeInt(1);
             dest.writeCharSequence(mExplanation);
         } else {
             dest.writeInt(0);
         }
-        dest.writeParcelable(mReference, flags);
         dest.writeBundle(mSignals);
         dest.writeInt(mUser);
     }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 37674a6..1bc605f 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -16,11 +16,11 @@
 
 package android.service.notification;
 
+import android.app.NotificationChannel;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 
-import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.SdkConstant;
 import android.app.INotificationManager;
@@ -49,8 +49,7 @@
 import android.widget.RemoteViews;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.SomeArgs;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -1112,7 +1111,10 @@
         }
     }
 
-    private void applyUpdateLocked(NotificationRankingUpdate update) {
+    /**
+     * @hide
+     */
+    public final void applyUpdateLocked(NotificationRankingUpdate update) {
         mRankingMap = new RankingMap(update);
     }
 
@@ -1148,6 +1150,12 @@
         private CharSequence mImportanceExplanation;
         // System specified group key.
         private String mOverrideGroupKey;
+        // Notification assistant channel override.
+        private NotificationChannel mOverrideChannel;
+        // Notification assistant people override.
+        private ArrayList<String> mOverridePeople;
+        // Notification assistant snooze criteria.
+        private ArrayList<SnoozeCriterion> mSnoozeCriteria;
 
         public Ranking() {}
 
@@ -1217,7 +1225,7 @@
         }
 
         /**
-         * If the importance has been overriden by user preference, then this will be non-null,
+         * If the importance has been overridden by user preference, then this will be non-null,
          * and should be displayed to the user.
          *
          * @return the explanation for the importance, or null if it is the natural importance
@@ -1227,16 +1235,44 @@
         }
 
         /**
-         * If the system has overriden the group key, then this will be non-null, and this
+         * If the system has overridden the group key, then this will be non-null, and this
          * key should be used to bundle notifications.
          */
         public String getOverrideGroupKey() {
             return mOverrideGroupKey;
         }
 
+        /**
+         * If the {@link NotificationAssistantService} has overridden the channel this notification
+         * was posted to, then this will not match the channel provided by the posting application
+         * and this should be used to determine the interruptiveness of the notification instead.
+         */
+        public NotificationChannel getChannel() {
+            return mOverrideChannel;
+        }
+
+        /**
+         * If the {@link NotificationAssistantService} has added people to this notification, then
+         * this will be non-null.
+         */
+        public List<String> getAdditionalPeople() {
+            return mOverridePeople;
+        }
+
+        /**
+         * Returns snooze criteria provided by the {@link NotificationAssistantService}. If your
+         * user interface displays options for snoozing notifications these criteria should be
+         * displayed as well.
+         */
+        public List<SnoozeCriterion> getSnoozeCriteria() {
+            return mSnoozeCriteria;
+        }
+
         private void populate(String key, int rank, boolean matchesInterruptionFilter,
                 int visibilityOverride, int suppressedVisualEffects, int importance,
-                CharSequence explanation, String overrideGroupKey) {
+                CharSequence explanation, String overrideGroupKey,
+                NotificationChannel overrideChannel, ArrayList<String> overridePeople,
+                ArrayList<SnoozeCriterion> snoozeCriteria) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1246,6 +1282,9 @@
             mImportance = importance;
             mImportanceExplanation = explanation;
             mOverrideGroupKey = overrideGroupKey;
+            mOverrideChannel = overrideChannel;
+            mOverridePeople = overridePeople;
+            mSnoozeCriteria = snoozeCriteria;
         }
 
         /**
@@ -1289,6 +1328,9 @@
         private ArrayMap<String, Integer> mImportance;
         private ArrayMap<String, String> mImportanceExplanation;
         private ArrayMap<String, String> mOverrideGroupKeys;
+        private ArrayMap<String, NotificationChannel> mOverrideChannels;
+        private ArrayMap<String, ArrayList<String>> mOverridePeople;
+        private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1315,7 +1357,8 @@
             int rank = getRank(key);
             outRanking.populate(key, rank, !isIntercepted(key),
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
-                    getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key));
+                    getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
+                    getOverrideChannel(key), getOverridePeople(key), getSnoozeCriteria(key));
             return rank >= 0;
         }
 
@@ -1395,6 +1438,33 @@
             return mOverrideGroupKeys.get(key);
         }
 
+        private NotificationChannel getOverrideChannel(String key) {
+            synchronized (this) {
+                if (mOverrideChannels == null) {
+                    buildOverrideChannelsLocked();
+                }
+            }
+            return mOverrideChannels.get(key);
+        }
+
+        private ArrayList<String> getOverridePeople(String key) {
+            synchronized (this) {
+                if (mOverridePeople == null) {
+                    buildOverridePeopleLocked();
+                }
+            }
+            return mOverridePeople.get(key);
+        }
+
+        private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key) {
+            synchronized (this) {
+                if (mSnoozeCriteria == null) {
+                    buildSnoozeCriteriaLocked();
+                }
+            }
+            return mSnoozeCriteria.get(key);
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1458,6 +1528,33 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildOverrideChannelsLocked() {
+            Bundle overrideChannels = mRankingUpdate.getOverrideChannels();
+            mOverrideChannels = new ArrayMap<>(overrideChannels.size());
+            for (String key : overrideChannels.keySet()) {
+                mOverrideChannels.put(key, overrideChannels.getParcelable(key));
+            }
+        }
+
+        // Locked by 'this'
+        private void buildOverridePeopleLocked() {
+            Bundle overridePeople = mRankingUpdate.getOverridePeople();
+            mOverridePeople = new ArrayMap<>(overridePeople.size());
+            for (String key : overridePeople.keySet()) {
+                mOverridePeople.put(key, overridePeople.getStringArrayList(key));
+            }
+        }
+
+        // Locked by 'this'
+        private void buildSnoozeCriteriaLocked() {
+            Bundle snoozeCriteria = mRankingUpdate.getSnoozeCriteria();
+            mSnoozeCriteria = new ArrayMap<>(snoozeCriteria.size());
+            for (String key : snoozeCriteria.keySet()) {
+                mSnoozeCriteria.put(key, snoozeCriteria.getParcelableArrayList(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 788b5c0..a2cdeff 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -31,10 +31,14 @@
     private final int[] mImportance;
     private final Bundle mImportanceExplanation;
     private final Bundle mOverrideGroupKeys;
+    private final Bundle mOverrideChannels;
+    private final Bundle mOverridePeople;
+    private final Bundle mSnoozeCriteria;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
-            int[] importance, Bundle explanation, Bundle overrideGroupKeys) {
+            int[] importance, Bundle explanation, Bundle overrideGroupKeys,
+            Bundle overrideChannels, Bundle overridePeople, Bundle snoozeCriteria) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -42,6 +46,9 @@
         mImportance = importance;
         mImportanceExplanation = explanation;
         mOverrideGroupKeys = overrideGroupKeys;
+        mOverrideChannels = overrideChannels;
+        mOverridePeople = overridePeople;
+        mSnoozeCriteria = snoozeCriteria;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -53,6 +60,9 @@
         in.readIntArray(mImportance);
         mImportanceExplanation = in.readBundle();
         mOverrideGroupKeys = in.readBundle();
+        mOverrideChannels = in.readBundle();
+        mOverridePeople = in.readBundle();
+        mSnoozeCriteria = in.readBundle();
     }
 
     @Override
@@ -69,6 +79,9 @@
         out.writeIntArray(mImportance);
         out.writeBundle(mImportanceExplanation);
         out.writeBundle(mOverrideGroupKeys);
+        out.writeBundle(mOverrideChannels);
+        out.writeBundle(mOverridePeople);
+        out.writeBundle(mSnoozeCriteria);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -109,4 +122,16 @@
     public Bundle getOverrideGroupKeys() {
         return mOverrideGroupKeys;
     }
+
+    public Bundle getOverrideChannels() {
+        return mOverrideChannels;
+    }
+
+    public Bundle getOverridePeople() {
+        return mOverridePeople;
+    }
+
+    public Bundle getSnoozeCriteria() {
+        return mSnoozeCriteria;
+    }
 }
diff --git a/core/java/android/service/notification/SnoozeCriterion.aidl b/core/java/android/service/notification/SnoozeCriterion.aidl
new file mode 100644
index 0000000..9b666d8
--- /dev/null
+++ b/core/java/android/service/notification/SnoozeCriterion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable SnoozeCriterion;
\ No newline at end of file
diff --git a/core/java/android/service/notification/SnoozeCriterion.java b/core/java/android/service/notification/SnoozeCriterion.java
new file mode 100644
index 0000000..f37f1ae
--- /dev/null
+++ b/core/java/android/service/notification/SnoozeCriterion.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.notification;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents an option to be shown to users for snoozing a notification until a given context
+ * instead of for a fixed amount of time.
+ */
+public final class SnoozeCriterion implements Parcelable {
+    private final String mId;
+    private final CharSequence mExplanation;
+    private final CharSequence mConfirmation;
+
+    public SnoozeCriterion(String id, CharSequence explanation, CharSequence confirmation) {
+        mId = id;
+        mExplanation = explanation;
+        mConfirmation = confirmation;
+    }
+
+    protected SnoozeCriterion(Parcel in) {
+        if (in.readByte() != 0) {
+            mId = in.readString();
+        } else {
+            mId = null;
+        }
+        if (in.readByte() != 0) {
+            mExplanation = in.readCharSequence();
+        } else {
+            mExplanation = null;
+        }
+        if (in.readByte() != 0) {
+            mConfirmation = in.readCharSequence();
+        } else {
+            mConfirmation = null;
+        }
+    }
+
+    /**
+     * Returns the id of this criterion.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the user visible explanation of how long a notification will be snoozed if
+     * this criterion is chosen.
+     */
+    public CharSequence getExplanation() {
+        return mExplanation;
+    }
+
+    /**
+     * Returns the user visible confirmation message shown when this criterion is chosen.
+     */
+    public CharSequence getConfirmation() {
+        return mConfirmation;
+    }
+
+    public static final Creator<SnoozeCriterion> CREATOR = new Creator<SnoozeCriterion>() {
+        @Override
+        public SnoozeCriterion createFromParcel(Parcel in) {
+            return new SnoozeCriterion(in);
+        }
+
+        @Override
+        public SnoozeCriterion[] newArray(int size) {
+            return new SnoozeCriterion[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mId != null) {
+            dest.writeByte((byte) 1);
+            dest.writeString(mId);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        if (mExplanation != null) {
+            dest.writeByte((byte) 1);
+            dest.writeCharSequence(mExplanation);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        if (mConfirmation != null) {
+            dest.writeByte((byte) 1);
+            dest.writeCharSequence(mConfirmation);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        SnoozeCriterion that = (SnoozeCriterion) o;
+
+        if (mId != null ? !mId.equals(that.mId) : that.mId != null) return false;
+        if (mExplanation != null ? !mExplanation.equals(that.mExplanation)
+                : that.mExplanation != null) {
+            return false;
+        }
+        return mConfirmation != null ? mConfirmation.equals(that.mConfirmation)
+                : that.mConfirmation == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId != null ? mId.hashCode() : 0;
+        result = 31 * result + (mExplanation != null ? mExplanation.hashCode() : 0);
+        result = 31 * result + (mConfirmation != null ? mConfirmation.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index ccaf204..749cf08 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -19,7 +19,6 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import android.os.Process;
 
 import libcore.io.IoUtils;
 import dalvik.system.CloseGuard;
@@ -37,13 +36,13 @@
  * each other.
  * <p>
  * The data structure is designed to have one owner process that can
- * read/write. There may be multiple client processes that can only read or
- * read/write depending how the data structure was configured when
- * instantiated. The owner process is the process that created the array.
- * The shared memory is pinned (not reclaimed by the system) until the
- * owning process dies or the data structure is closed. This class
- * is <strong>not</strong> thread safe. You should not interact with
- * an instance of this class once it is closed.
+ * read/write. There may be multiple client processes that can only read.
+ * The owner process is the process that created the array. The shared
+ * memory is pinned (not reclaimed by the system) until the owning process
+ * dies or the data structure is closed. This class is <strong>not</strong>
+ * thread safe. You should not interact with an instance of this class
+ * once it is closed. If you pass back to the owner process an instance
+ * it will be read only even in the owning process.
  * </p>
  *
  * @hide
@@ -55,8 +54,7 @@
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
-    private final int mOwnerPid;
-    private final boolean mClientWritable;
+    private final boolean mIsOwner;
     private final long mMemoryAddr;
     private int mFd;
 
@@ -65,35 +63,27 @@
      *
      * @param size The size of the array in terms of integer slots. Cannot be
      *     more than {@link #getMaxSize()}.
-     * @param clientWritable Whether other processes can write to the array.
      * @throws IOException If an error occurs while accessing the shared memory.
      */
-    public MemoryIntArray(int size, boolean clientWritable) throws IOException {
+    public MemoryIntArray(int size) throws IOException {
         if (size > MAX_SIZE) {
             throw new IllegalArgumentException("Max size is " + MAX_SIZE);
         }
-        mOwnerPid = Process.myPid();
-        mClientWritable = clientWritable;
+        mIsOwner = true;
         final String name = UUID.randomUUID().toString();
         mFd = nativeCreate(name, size);
-        mMemoryAddr = nativeOpen(mFd, true, clientWritable);
+        mMemoryAddr = nativeOpen(mFd, mIsOwner);
         mCloseGuard.open("close");
     }
 
     private MemoryIntArray(Parcel parcel) throws IOException {
-        mOwnerPid = parcel.readInt();
-        mClientWritable = (parcel.readInt() == 1);
+        mIsOwner = false;
         ParcelFileDescriptor pfd = parcel.readParcelable(null);
         if (pfd == null) {
             throw new IOException("No backing file descriptor");
         }
         mFd = pfd.detachFd();
-        final long memoryAddress = parcel.readLong();
-        if (isOwner()) {
-            mMemoryAddr = memoryAddress;
-        } else {
-            mMemoryAddr = nativeOpen(mFd, false, mClientWritable);
-        }
+        mMemoryAddr = nativeOpen(mFd, mIsOwner);
         mCloseGuard.open("close");
     }
 
@@ -102,7 +92,7 @@
      */
     public boolean isWritable() {
         enforceNotClosed();
-        return isOwner() || mClientWritable;
+        return mIsOwner;
     }
 
     /**
@@ -115,7 +105,7 @@
     public int get(int index) throws IOException {
         enforceNotClosed();
         enforceValidIndex(index);
-        return nativeGet(mFd, mMemoryAddr, index, isOwner());
+        return nativeGet(mFd, mMemoryAddr, index);
     }
 
     /**
@@ -131,7 +121,7 @@
         enforceNotClosed();
         enforceWritable();
         enforceValidIndex(index);
-        nativeSet(mFd, mMemoryAddr, index, value, isOwner());
+        nativeSet(mFd, mMemoryAddr, index, value);
     }
 
     /**
@@ -152,7 +142,7 @@
     @Override
     public void close() throws IOException {
         if (!isClosed()) {
-            nativeClose(mFd, mMemoryAddr, isOwner());
+            nativeClose(mFd, mMemoryAddr, mIsOwner);
             mFd = -1;
             mCloseGuard.close();
         }
@@ -184,11 +174,8 @@
     public void writeToParcel(Parcel parcel, int flags) {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd);
         try {
-            parcel.writeInt(mOwnerPid);
-            parcel.writeInt(mClientWritable ? 1 : 0);
             // Don't let writing to a parcel to close our fd - plz
             parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-            parcel.writeLong(mMemoryAddr);
         } finally {
             pfd.detachFd();
         }
@@ -214,10 +201,6 @@
         return mFd;
     }
 
-    private boolean isOwner() {
-        return mOwnerPid == Process.myPid();
-    }
-
     private void enforceNotClosed() {
         if (isClosed()) {
             throw new IllegalStateException("cannot interact with a closed instance");
@@ -239,10 +222,10 @@
     }
 
     private native int nativeCreate(String name, int size);
-    private native long nativeOpen(int fd, boolean owner, boolean writable);
+    private native long nativeOpen(int fd, boolean owner);
     private native void nativeClose(int fd, long memoryAddr, boolean owner);
-    private native int nativeGet(int fd, long memoryAddr, int index, boolean owner);
-    private native void nativeSet(int fd, long memoryAddr, int index, int value, boolean owner);
+    private native int nativeGet(int fd, long memoryAddr, int index);
+    private native void nativeSet(int fd, long memoryAddr, int index, int value);
     private native int nativeSize(int fd);
 
     /**
@@ -259,8 +242,7 @@
             try {
                 return new MemoryIntArray(parcel);
             } catch (IOException ioe) {
-                Log.e(TAG, "Error unparceling MemoryIntArray");
-                return null;
+                throw new IllegalArgumentException("Error unparceling MemoryIntArray");
             }
         }
 
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 8f99399..81251fc 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -19,6 +19,10 @@
 import android.annotation.TestApi;
 import android.util.Log;
 
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.List;
 
@@ -179,6 +183,11 @@
     private EncodedBuffer mBuffer;
 
     /**
+     * Our stream.  If there is one.
+     */
+    private OutputStream mStream;
+
+    /**
      * Current nesting depth of startObject calls.
      */
     private int mDepth;
@@ -226,6 +235,690 @@
         mBuffer = new EncodedBuffer(chunkSize);
     }
 
+    /**
+     * Construct a ProtoOutputStream that sits on top of an OutputStream.
+     * @more
+     * The {@link #flush() flush()} method must be called when done writing
+     * to flush any remanining data, althought data *may* be written at intermediate
+     * points within the writing as well.
+     */
+    public ProtoOutputStream(OutputStream stream) {
+        this();
+        mStream = stream;
+    }
+
+    /**
+     * Construct a ProtoOutputStream that sits on top of a FileDescriptor.
+     * @more
+     * The {@link #flush() flush()} method must be called when done writing
+     * to flush any remanining data, althought data *may* be written at intermediate
+     * points within the writing as well.
+     */
+    public ProtoOutputStream(FileDescriptor fd) {
+        this(new FileOutputStream(fd));
+    }
+
+    /**
+     * Write a value for the given fieldId.
+     *
+     * Will automatically convert for the following field types, and
+     * throw an exception for others: double, float, int32, int64, uint32, uint64,
+     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, double val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // double
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeDoubleImpl(id, (double)val);
+                break;
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedDoubleImpl(id, (double)val);
+                break;
+            // float
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFloatImpl(id, (float)val);
+                break;
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFloatImpl(id, (float)val);
+                break;
+            // int32
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt32Impl(id, (int)val);
+                break;
+            // int64
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt64Impl(id, (long)val);
+                break;
+            // uint32
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt32Impl(id, (int)val);
+                break;
+            // uint64
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt64Impl(id, (long)val);
+                break;
+            // sint32
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt32Impl(id, (int)val);
+                break;
+            // sint64
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt64Impl(id, (long)val);
+                break;
+            // fixed32
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed32Impl(id, (int)val);
+                break;
+            // fixed64
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed64Impl(id, (long)val);
+                break;
+            // sfixed32
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed32Impl(id, (int)val);
+                break;
+            // sfixed64
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed64Impl(id, (long)val);
+                break;
+            // bool
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeBoolImpl(id, val != 0);
+                break;
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedBoolImpl(id, val != 0);
+                break;
+            // enum
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeEnumImpl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedEnumImpl(id, (int)val);
+                break;
+            // string, bytes, object not allowed here.
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, double) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Write a value for the given fieldId.
+     *
+     * Will automatically convert for the following field types, and
+     * throw an exception for others: double, float, int32, int64, uint32, uint64,
+     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, float val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // double
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeDoubleImpl(id, (double)val);
+                break;
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedDoubleImpl(id, (double)val);
+                break;
+            // float
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFloatImpl(id, (float)val);
+                break;
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFloatImpl(id, (float)val);
+                break;
+            // int32
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt32Impl(id, (int)val);
+                break;
+            // int64
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt64Impl(id, (long)val);
+                break;
+            // uint32
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt32Impl(id, (int)val);
+                break;
+            // uint64
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt64Impl(id, (long)val);
+                break;
+            // sint32
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt32Impl(id, (int)val);
+                break;
+            // sint64
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt64Impl(id, (long)val);
+                break;
+            // fixed32
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed32Impl(id, (int)val);
+                break;
+            // fixed64
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed64Impl(id, (long)val);
+                break;
+            // sfixed32
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed32Impl(id, (int)val);
+                break;
+            // sfixed64
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed64Impl(id, (long)val);
+                break;
+            // bool
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeBoolImpl(id, val != 0);
+                break;
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedBoolImpl(id, val != 0);
+                break;
+            // enum
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeEnumImpl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedEnumImpl(id, (int)val);
+                break;
+            // string, bytes, object not allowed here.
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, float) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Write a value for the given fieldId.
+     *
+     * Will automatically convert for the following field types, and
+     * throw an exception for others: double, float, int32, int64, uint32, uint64,
+     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, int val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // double
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeDoubleImpl(id, (double)val);
+                break;
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedDoubleImpl(id, (double)val);
+                break;
+            // float
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFloatImpl(id, (float)val);
+                break;
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFloatImpl(id, (float)val);
+                break;
+            // int32
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt32Impl(id, (int)val);
+                break;
+            // int64
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt64Impl(id, (long)val);
+                break;
+            // uint32
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt32Impl(id, (int)val);
+                break;
+            // uint64
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt64Impl(id, (long)val);
+                break;
+            // sint32
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt32Impl(id, (int)val);
+                break;
+            // sint64
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt64Impl(id, (long)val);
+                break;
+            // fixed32
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed32Impl(id, (int)val);
+                break;
+            // fixed64
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed64Impl(id, (long)val);
+                break;
+            // sfixed32
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed32Impl(id, (int)val);
+                break;
+            // sfixed64
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed64Impl(id, (long)val);
+                break;
+            // bool
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeBoolImpl(id, val != 0);
+                break;
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedBoolImpl(id, val != 0);
+                break;
+            // enum
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeEnumImpl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedEnumImpl(id, (int)val);
+                break;
+            // string, bytes, object not allowed here.
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, int) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Write a value for the given fieldId.
+     *
+     * Will automatically convert for the following field types, and
+     * throw an exception for others: double, float, int32, int64, uint32, uint64,
+     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, long val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // double
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeDoubleImpl(id, (double)val);
+                break;
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedDoubleImpl(id, (double)val);
+                break;
+            // float
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFloatImpl(id, (float)val);
+                break;
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFloatImpl(id, (float)val);
+                break;
+            // int32
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt32Impl(id, (int)val);
+                break;
+            // int64
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedInt64Impl(id, (long)val);
+                break;
+            // uint32
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt32Impl(id, (int)val);
+                break;
+            // uint64
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeUInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedUInt64Impl(id, (long)val);
+                break;
+            // sint32
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt32Impl(id, (int)val);
+                break;
+            // sint64
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSInt64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSInt64Impl(id, (long)val);
+                break;
+            // fixed32
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed32Impl(id, (int)val);
+                break;
+            // fixed64
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedFixed64Impl(id, (long)val);
+                break;
+            // sfixed32
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed32Impl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed32Impl(id, (int)val);
+                break;
+            // sfixed64
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeSFixed64Impl(id, (long)val);
+                break;
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedSFixed64Impl(id, (long)val);
+                break;
+            // bool
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeBoolImpl(id, val != 0);
+                break;
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedBoolImpl(id, val != 0);
+                break;
+            // enum
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeEnumImpl(id, (int)val);
+                break;
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedEnumImpl(id, (int)val);
+                break;
+            // string, bytes, object not allowed here.
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, long) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Write a boolean value for the given fieldId.
+     *
+     * If the field is not a bool field, an exception will be thrown.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, boolean val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // bool
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeBoolImpl(id, val);
+                break;
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedBoolImpl(id, val);
+                break;
+            // nothing else allowed
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, boolean) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Write a string value for the given fieldId.
+     *
+     * If the field is not a string field, an exception will be thrown.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, String val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // string
+            case (int)((FIELD_TYPE_STRING | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeStringImpl(id, val);
+                break;
+            case (int)((FIELD_TYPE_STRING | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_STRING | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedStringImpl(id, val);
+                break;
+            // nothing else allowed
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, String) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Write a byte[] value for the given fieldId.
+     *
+     * If the field is not a bytes or object field, an exception will be thrown.
+     *
+     * @param fieldId The field identifier constant from the generated class.
+     * @param val The value.
+     */
+    public void write(long fieldId, byte[] val) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+            // bytes
+            case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeBytesImpl(id, val);
+                break;
+            case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedBytesImpl(id, val);
+                break;
+            // Object
+            case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+                writeObjectImpl(id, val);
+                break;
+            case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+            case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+                writeRepeatedObjectImpl(id, val);
+                break;
+            // nothing else allowed
+            default: {
+                throw new IllegalArgumentException("Attempt to call write(long, byte[]) with "
+                        + getFieldIdString(fieldId));
+            }
+        }
+    }
+
+    /**
+     * Start a sub object.
+     */
+    public long start(long fieldId) {
+        assertNotCompacted();
+        final int id = (int)fieldId;
+
+        if ((fieldId & FIELD_TYPE_MASK) == FIELD_TYPE_OBJECT) {
+            final long count = fieldId & FIELD_COUNT_MASK;
+            if (count == FIELD_COUNT_SINGLE) {
+                return startObjectImpl(id, false);
+            } else if (count == FIELD_COUNT_REPEATED || count == FIELD_COUNT_PACKED) {
+                return startObjectImpl(id, true);
+            }
+        }
+        throw new IllegalArgumentException("Attempt to call start(long) with "
+                + getFieldIdString(fieldId));
+    }
+
+    /**
+     * End the object started by start() that returned token.
+     */
+    public void end(long token) {
+        endObjectImpl(token, getRepeatedFromToken(token));
+    }
+
     //
     // proto3 type: double
     // java type: double
@@ -240,6 +933,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE);
 
+        writeDoubleImpl(id, val);
+    }
+
+    private void writeDoubleImpl(int id, double val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_FIXED64);
             mBuffer.writeRawFixed64(Double.doubleToLongBits(val));
@@ -253,6 +950,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_DOUBLE);
 
+        writeRepeatedDoubleImpl(id, val);
+    }
+
+    private void writeRepeatedDoubleImpl(int id, double val) {
         writeTag(id, WIRE_TYPE_FIXED64);
         mBuffer.writeRawFixed64(Double.doubleToLongBits(val));
     }
@@ -287,6 +988,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT);
 
+        writeFloatImpl(id, val);
+    }
+
+    private void writeFloatImpl(int id, float val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_FIXED32);
             mBuffer.writeRawFixed32(Float.floatToIntBits(val));
@@ -300,6 +1005,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FLOAT);
 
+        writeRepeatedFloatImpl(id, val);
+    }
+
+    private void writeRepeatedFloatImpl(int id, float val) {
         writeTag(id, WIRE_TYPE_FIXED32);
         mBuffer.writeRawFixed32(Float.floatToIntBits(val));
     }
@@ -357,6 +1066,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_INT32);
 
+        writeInt32Impl(id, val);
+    }
+
+    private void writeInt32Impl(int id, int val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             writeUnsignedVarintFromSignedInt(val);
@@ -374,6 +1087,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_INT32);
 
+        writeRepeatedInt32Impl(id, val);
+    }
+
+    private void writeRepeatedInt32Impl(int id, int val) {
         writeTag(id, WIRE_TYPE_VARINT);
         writeUnsignedVarintFromSignedInt(val);
     }
@@ -418,6 +1135,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_INT64);
 
+        writeInt64Impl(id, val);
+    }
+
+    private void writeInt64Impl(int id, long val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             mBuffer.writeRawVarint64(val);
@@ -431,6 +1152,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_INT64);
 
+        writeRepeatedInt64Impl(id, val);
+    }
+
+    private void writeRepeatedInt64Impl(int id, long val) {
         writeTag(id, WIRE_TYPE_VARINT);
         mBuffer.writeRawVarint64(val);
     }
@@ -470,6 +1195,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32);
 
+        writeUInt32Impl(id, val);
+    }
+
+    private void writeUInt32Impl(int id, int val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             mBuffer.writeRawVarint32(val);
@@ -483,6 +1212,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_UINT32);
 
+        writeRepeatedUInt32Impl(id, val);
+    }
+
+    private void writeRepeatedUInt32Impl(int id, int val) {
         writeTag(id, WIRE_TYPE_VARINT);
         mBuffer.writeRawVarint32(val);
     }
@@ -522,6 +1255,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64);
 
+        writeUInt64Impl(id, val);
+    }
+
+    private void writeUInt64Impl(int id, long val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             mBuffer.writeRawVarint64(val);
@@ -535,6 +1272,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_UINT64);
 
+        writeRepeatedUInt64Impl(id, val);
+    }
+
+    private void writeRepeatedUInt64Impl(int id, long val) {
         writeTag(id, WIRE_TYPE_VARINT);
         mBuffer.writeRawVarint64(val);
     }
@@ -574,6 +1315,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32);
 
+        writeSInt32Impl(id, val);
+    }
+
+    private void writeSInt32Impl(int id, int val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             mBuffer.writeRawZigZag32(val);
@@ -587,6 +1332,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SINT32);
 
+        writeRepeatedSInt32Impl(id, val);
+    }
+
+    private void writeRepeatedSInt32Impl(int id, int val) {
         writeTag(id, WIRE_TYPE_VARINT);
         mBuffer.writeRawZigZag32(val);
     }
@@ -626,6 +1375,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64);
 
+        writeSInt64Impl(id, val);
+    }
+
+    private void writeSInt64Impl(int id, long val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             mBuffer.writeRawZigZag64(val);
@@ -639,6 +1392,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SINT64);
 
+        writeRepeatedSInt64Impl(id, val);
+    }
+
+    private void writeRepeatedSInt64Impl(int id, long val) {
         writeTag(id, WIRE_TYPE_VARINT);
         mBuffer.writeRawZigZag64(val);
     }
@@ -677,6 +1434,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32);
 
+        writeFixed32Impl(id, val);
+    }
+
+    private void writeFixed32Impl(int id, int val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_FIXED32);
             mBuffer.writeRawFixed32(val);
@@ -690,6 +1451,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FIXED32);
 
+        writeRepeatedFixed32Impl(id, val);
+    }
+
+    private void writeRepeatedFixed32Impl(int id, int val) {
         writeTag(id, WIRE_TYPE_FIXED32);
         mBuffer.writeRawFixed32(val);
     }
@@ -724,6 +1489,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64);
 
+        writeFixed64Impl(id, val);
+    }
+
+    private void writeFixed64Impl(int id, long val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_FIXED64);
             mBuffer.writeRawFixed64(val);
@@ -737,6 +1506,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FIXED64);
 
+        writeRepeatedFixed64(id, val);
+    }
+
+    private void writeRepeatedFixed64Impl(int id, long val) {
         writeTag(id, WIRE_TYPE_FIXED64);
         mBuffer.writeRawFixed64(val);
     }
@@ -770,6 +1543,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32);
 
+        writeSFixed32Impl(id, val);
+    }
+
+    private void writeSFixed32Impl(int id, int val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_FIXED32);
             mBuffer.writeRawFixed32(val);
@@ -783,6 +1560,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SFIXED32);
 
+        writeRepeatedSFixed32Impl(id, val);
+    }
+
+    private void writeRepeatedSFixed32Impl(int id, int val) {
         writeTag(id, WIRE_TYPE_FIXED32);
         mBuffer.writeRawFixed32(val);
     }
@@ -817,6 +1598,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64);
 
+        writeSFixed64Impl(id, val);
+    }
+
+    private void writeSFixed64Impl(int id, long val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_FIXED64);
             mBuffer.writeRawFixed64(val);
@@ -830,6 +1615,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SFIXED64);
 
+        writeRepeatedSFixed64(id, val);
+    }
+
+    private void writeRepeatedSFixed64Impl(int id, long val) {
         writeTag(id, WIRE_TYPE_FIXED64);
         mBuffer.writeRawFixed64(val);
     }
@@ -864,6 +1653,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL);
 
+        writeBoolImpl(id, val);
+    }
+
+    private void writeBoolImpl(int id, boolean val) {
         if (val) {
             writeTag(id, WIRE_TYPE_VARINT);
             // 0 and 1 are the same as their varint counterparts
@@ -878,6 +1671,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BOOL);
 
+        writeRepeatedBool(id, val);
+    }
+
+    private void writeRepeatedBoolImpl(int id, boolean val) {
         writeTag(id, WIRE_TYPE_VARINT);
         mBuffer.writeRawByte((byte)(val ? 1 : 0));
     }
@@ -916,6 +1713,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_STRING);
 
+        writeStringImpl(id, val);
+    }
+
+    private void writeStringImpl(int id, String val) {
         if (val != null && val.length() > 0) {
             writeUtf8String(id, val);
         }
@@ -928,6 +1729,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_STRING);
 
+        writeRepeatedStringImpl(id, val);
+    }
+
+    private void writeRepeatedStringImpl(int id, String val) {
         if (val == null || val.length() == 0) {
             writeKnownLengthHeader(id, 0);
         } else {
@@ -963,6 +1768,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES);
 
+        writeBytesImpl(id, val);
+    }
+
+    private void writeBytesImpl(int id, byte[] val) {
         if (val != null && val.length > 0) {
             writeKnownLengthHeader(id, val.length);
             mBuffer.writeRawBuffer(val);
@@ -976,6 +1785,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BYTES);
 
+        writeRepeatedBytesImpl(id, val);
+    }
+
+    private void writeRepeatedBytesImpl(int id, byte[] val) {
         writeKnownLengthHeader(id, val == null ? 0 : val.length);
         mBuffer.writeRawBuffer(val);
     }
@@ -995,6 +1808,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM);
 
+        writeEnumImpl(id, val);
+    }
+
+    private void writeEnumImpl(int id, int val) {
         if (val != 0) {
             writeTag(id, WIRE_TYPE_VARINT);
             writeUnsignedVarintFromSignedInt(val);
@@ -1008,6 +1825,10 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_ENUM);
 
+        writeRepeatedEnumImpl(id, val);
+    }
+
+    private void writeRepeatedEnumImpl(int id, int val) {
         writeTag(id, WIRE_TYPE_VARINT);
         writeUnsignedVarintFromSignedInt(val);
     }
@@ -1239,6 +2060,38 @@
         }
     }
 
+    /**
+     * Write an object that has already been flattend.
+     */
+    public void writeObject(long fieldId, byte[] value) {
+        assertNotCompacted();
+        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_OBJECT);
+
+        writeObjectImpl(id, value);
+    }
+
+    public void writeObjectImpl(int id, byte[] value) {
+        if (value != null && value.length != 0) {
+            writeKnownLengthHeader(id, value.length);
+            mBuffer.writeRawBuffer(value);
+        }
+    }
+
+    /**
+     * Write an object that has already been flattend.
+     */
+    public void writeRepeatedObject(long fieldId, byte[] value) {
+        assertNotCompacted();
+        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_OBJECT);
+
+        writeRepeatedObjectImpl(id, value);
+    }
+
+    public void writeRepeatedObjectImpl(int id, byte[] value) {
+        writeKnownLengthHeader(id, value == null ? 0 : value.length);
+        mBuffer.writeRawBuffer(value);
+    }
+
     //
     // Tags
     //
@@ -1358,6 +2211,25 @@
         }
     }
 
+    /**
+     * Get a debug string for a fieldId.
+     */
+    private String getFieldIdString(long fieldId) {
+        final long fieldCount = fieldId & FIELD_COUNT_MASK;
+        String countString = getFieldCountString(fieldCount);
+        if (countString == null) {
+            countString = "fieldCount=" + fieldCount;
+        }
+
+        final long fieldType = fieldId & FIELD_TYPE_MASK;
+        String typeString = getFieldTypeString(fieldType);
+        if (typeString == null) {
+            typeString = "fieldType=" + fieldType;
+        }
+
+        return fieldCount + " " + typeString + " tag=" + ((int)fieldId)
+                + " fieldId=0x" + Long.toHexString(fieldId);
+    }
 
     /**
      * Return how many bytes an encoded field tag will require.
@@ -1580,6 +2452,37 @@
     }
 
     /**
+     * Write remaining data to the output stream.  If there is no output stream,
+     * this function does nothing. Any currently open objects (i.e. ones that
+     * have not had endObject called for them will not be written).  Whether this
+     * writes objects that are closed if there are remaining open objects is
+     * undefined (current implementation does not write it, future ones will).
+     * For now, can either call getBytes() or flush(), but not both.
+     */
+    public void flush() {
+        if (mStream == null) {
+            return;
+        }
+        if (mDepth != 0) {
+            // TODO: The compacting code isn't ready yet to compact unless we're done.
+            // TODO: Fix that.
+            return;
+        }
+        if (mCompacted) {
+            // If we're compacted, we already wrote it finished.
+            return;
+        }
+        compactIfNecessary();
+        final byte[] data = mBuffer.getBytes(mBuffer.getReadableSize());
+        try {
+            mStream.write(data);
+            mStream.flush();
+        } catch (IOException ex) {
+            throw new RuntimeException("Error flushing proto to stream", ex);
+        }
+    }
+
+    /**
      * Read a raw tag from the buffer.
      */
     private int readRawTag() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f9a03c0..316b123 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2912,6 +2912,30 @@
     public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
 
     /**
+     * This flag was previously used for a private API. DO NOT reuse it for a public API as it might
+     * trigger undefined behavior on older platforms with apps compiled against a new SDK.
+     */
+    private static final int SYSTEM_UI_RESERVED_LEGACY1 = 0x00004000;
+
+    /**
+     * This flag was previously used for a private API. DO NOT reuse it for a public API as it might
+     * trigger undefined behavior on older platforms with apps compiled against a new SDK.
+     */
+    private static final int SYSTEM_UI_RESERVED_LEGACY2 = 0x00010000;
+
+    /**
+     * Flag for {@link #setSystemUiVisibility(int)}: Requests the navigation bar to draw in a mode
+     * that is compatible with light navigation bar backgrounds.
+     *
+     * <p>For this to take effect, the window must request
+     * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+     *         FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not
+     * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION
+     *         FLAG_TRANSLUCENT_NAVIGATION}.
+     */
+    public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 0x00000010;
+
+    /**
      * @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
      */
     @Deprecated
@@ -3104,7 +3128,7 @@
      *
      * Makes status bar transparent (but not the navigation bar).
      */
-    public static final int STATUS_BAR_TRANSPARENT = 0x0000008;
+    public static final int STATUS_BAR_TRANSPARENT = 0x00000008;
 
     /**
      * @hide
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 3171019..caa09cc 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -513,6 +513,11 @@
         void notifyShowingDreamChanged();
 
         /**
+         * @return The currently active input method window.
+         */
+        WindowState getInputMethodWindowLw();
+
+        /**
          * Notifies window manager that {@link #isKeyguardTrustedLw} has changed.
          */
         void notifyKeyguardTrustedChanged();
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index fc1520b..5fa1d2b 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1835,7 +1835,8 @@
         // can expect the OnAttachStateChangeListener to have been called prior
         // to executing this method, so we can rely on that instead.
         final Transition exitTransition = mExitTransition;
-        if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
+        if (exitTransition != null && decorView.isLaidOut()
+                && (mIsAnchorRootAttached || mAnchorRoot == null)) {
             // The decor view is non-interactive and non-IME-focusable during exit transitions.
             final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
             p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -1843,18 +1844,13 @@
             p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
             mWindowManager.updateViewLayout(decorView, p);
 
+            final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
+            final Rect epicenter = getTransitionEpicenter();
+
             // Once we start dismissing the decor view, all state (including
             // the anchor root) needs to be moved to the decor view since we
             // may open another popup while it's busy exiting.
-            final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
-            final Rect epicenter = getTransitionEpicenter();
-            exitTransition.setEpicenterCallback(new EpicenterCallback() {
-                @Override
-                public Rect onGetEpicenter(Transition transition) {
-                    return epicenter;
-                }
-            });
-            decorView.startExitTransition(exitTransition, anchorRoot,
+            decorView.startExitTransition(exitTransition, anchorRoot, epicenter,
                     new TransitionListenerAdapter() {
                         @Override
                         public void onTransitionEnd(Transition transition) {
@@ -2349,8 +2345,9 @@
          * its {@code onTransitionEnd} method called even if the transition
          * never starts; however, it may be called with a {@code null} argument.
          */
-        public void startExitTransition(Transition transition, final View anchorRoot,
-                final TransitionListener listener) {
+        public void startExitTransition(@NonNull Transition transition,
+                @Nullable final View anchorRoot, @Nullable final Rect epicenter,
+                @NonNull final TransitionListener listener) {
             if (transition == null) {
                 return;
             }
@@ -2358,24 +2355,35 @@
             // The anchor view's window may go away while we're executing our
             // transition, in which case we need to end the transition
             // immediately and execute the listener to remove the popup.
-            anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+            if (anchorRoot != null) {
+                anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+            }
 
             // The exit listener MUST be called for cleanup, even if the
             // transition never starts or ends. Stash it for later.
             mPendingExitListener = new TransitionListenerAdapter() {
                 @Override
-                public void onTransitionEnd(Transition transition) {
-                    anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
-                    listener.onTransitionEnd(transition);
+                public void onTransitionEnd(Transition t) {
+                    if (anchorRoot != null) {
+                        anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+                    }
+
+                    listener.onTransitionEnd(t);
 
                     // The listener was called. Our job here is done.
                     mPendingExitListener = null;
-                    transition.removeListener(this);
+                    t.removeListener(this);
                 }
             };
 
             final Transition exitTransition = transition.clone();
             exitTransition.addListener(mPendingExitListener);
+            exitTransition.setEpicenterCallback(new EpicenterCallback() {
+                @Override
+                public Rect onGetEpicenter(Transition transition) {
+                    return epicenter;
+                }
+            });
 
             final int count = getChildCount();
             for (int i = 0; i < count; i++) {
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
deleted file mode 100644
index 419c3d8..0000000
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.internal.os;
-
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.internal.util.Preconditions;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-/**
- * Represents a connection to {@code installd}. Allows multiple connect and
- * disconnect cycles.
- *
- * @hide for internal use only
- */
-public class InstallerConnection {
-    private static final String TAG = "InstallerConnection";
-    private static final boolean LOCAL_DEBUG = false;
-
-    private InputStream mIn;
-    private OutputStream mOut;
-    private LocalSocket mSocket;
-
-    private volatile Object mWarnIfHeld;
-
-    private final byte buf[] = new byte[1024];
-
-    public InstallerConnection() {
-    }
-
-    /**
-     * Yell loudly if someone tries making future calls while holding a lock on
-     * the given object.
-     */
-    public void setWarnIfHeld(Object warnIfHeld) {
-        Preconditions.checkState(mWarnIfHeld == null);
-        mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
-    }
-
-    public synchronized String transact(String cmd) {
-        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
-                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
-        }
-
-        if (!connect()) {
-            Slog.e(TAG, "connection failed");
-            return "-1";
-        }
-
-        if (!writeCommand(cmd)) {
-            /*
-             * If installd died and restarted in the background (unlikely but
-             * possible) we'll fail on the next write (this one). Try to
-             * reconnect and write the command one more time before giving up.
-             */
-            Slog.e(TAG, "write command failed? reconnect!");
-            if (!connect() || !writeCommand(cmd)) {
-                return "-1";
-            }
-        }
-        if (LOCAL_DEBUG) {
-            Slog.i(TAG, "send: '" + cmd + "'");
-        }
-
-        final int replyLength = readReply();
-        if (replyLength > 0) {
-            String s = new String(buf, 0, replyLength);
-            if (LOCAL_DEBUG) {
-                Slog.i(TAG, "recv: '" + s + "'");
-            }
-            return s;
-        } else {
-            if (LOCAL_DEBUG) {
-                Slog.i(TAG, "fail");
-            }
-            return "-1";
-        }
-    }
-
-    public String[] execute(String cmd, Object... args) throws InstallerException {
-        final StringBuilder builder = new StringBuilder(cmd);
-        for (Object arg : args) {
-            String escaped;
-            if (arg == null) {
-                escaped = "";
-            } else {
-                escaped = String.valueOf(arg);
-            }
-            if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
-                throw new InstallerException(
-                        "Invalid argument while executing " + cmd + " " + Arrays.toString(args));
-            }
-            if (TextUtils.isEmpty(escaped)) {
-                escaped = "!";
-            }
-            builder.append(' ').append(escaped);
-        }
-        final String[] resRaw = transact(builder.toString()).split(" ");
-        int res = -1;
-        try {
-            res = Integer.parseInt(resRaw[0]);
-        } catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) {
-        }
-        if (res != 0) {
-            throw new InstallerException(
-                    "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
-        }
-        return resRaw;
-    }
-
-    public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
-            int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries)
-            throws InstallerException {
-        dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /*outputPath*/, dexFlags,
-                compilerFilter, volumeUuid, sharedLibraries);
-    }
-
-    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter,
-            String volumeUuid, String sharedLibraries) throws InstallerException {
-        execute("dexopt",
-                apkPath,
-                uid,
-                pkgName,
-                instructionSet,
-                dexoptNeeded,
-                outputPath,
-                dexFlags,
-                compilerFilter,
-                volumeUuid,
-                sharedLibraries);
-    }
-
-    private boolean safeParseBooleanResult(String[] res) throws InstallerException {
-        if ((res == null) || (res.length != 2)) {
-            throw new InstallerException("Invalid size result: " + Arrays.toString(res));
-        }
-
-        // Just as a sanity check. Anything != "true" will be interpreted as false by parseBoolean.
-        if (!res[1].equals("true") && !res[1].equals("false")) {
-            throw new InstallerException("Invalid boolean result: " + Arrays.toString(res));
-        }
-
-        return Boolean.parseBoolean(res[1]);
-    }
-
-    public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
-        final String[] res = execute("merge_profiles", uid, pkgName);
-
-        return safeParseBooleanResult(res);
-    }
-
-    public boolean dumpProfiles(String gid, String packageName, String codePaths)
-            throws InstallerException {
-        final String[] res = execute("dump_profiles", gid, packageName, codePaths);
-
-        return safeParseBooleanResult(res);
-    }
-
-    private boolean connect() {
-        if (mSocket != null) {
-            return true;
-        }
-        Slog.i(TAG, "connecting...");
-        try {
-            mSocket = new LocalSocket();
-
-            LocalSocketAddress address = new LocalSocketAddress("installd",
-                    LocalSocketAddress.Namespace.RESERVED);
-
-            mSocket.connect(address);
-
-            mIn = mSocket.getInputStream();
-            mOut = mSocket.getOutputStream();
-        } catch (IOException ex) {
-            disconnect();
-            return false;
-        }
-        return true;
-    }
-
-    public void disconnect() {
-        Slog.i(TAG, "disconnecting...");
-        IoUtils.closeQuietly(mSocket);
-        IoUtils.closeQuietly(mIn);
-        IoUtils.closeQuietly(mOut);
-
-        mSocket = null;
-        mIn = null;
-        mOut = null;
-    }
-
-
-    private boolean readFully(byte[] buffer, int len) {
-        try {
-            Streams.readFully(mIn, buffer, 0, len);
-        } catch (IOException ioe) {
-            Slog.e(TAG, "read exception");
-            disconnect();
-            return false;
-        }
-
-        if (LOCAL_DEBUG) {
-            Slog.i(TAG, "read " + len + " bytes");
-        }
-
-        return true;
-    }
-
-    private int readReply() {
-        if (!readFully(buf, 2)) {
-            return -1;
-        }
-
-        final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if ((len < 1) || (len > buf.length)) {
-            Slog.e(TAG, "invalid reply length (" + len + ")");
-            disconnect();
-            return -1;
-        }
-
-        if (!readFully(buf, len)) {
-            return -1;
-        }
-
-        return len;
-    }
-
-    private boolean writeCommand(String cmdString) {
-        final byte[] cmd = cmdString.getBytes();
-        final int len = cmd.length;
-        if ((len < 1) || (len > buf.length)) {
-            return false;
-        }
-
-        buf[0] = (byte) (len & 0xff);
-        buf[1] = (byte) ((len >> 8) & 0xff);
-        try {
-            mOut.write(buf, 0, 2);
-            mOut.write(cmd, 0, len);
-        } catch (IOException ex) {
-            Slog.e(TAG, "write error");
-            disconnect();
-            return false;
-        }
-        return true;
-    }
-
-    public void waitForConnection() {
-        for (;;) {
-            try {
-                execute("ping");
-                return;
-            } catch (InstallerException ignored) {
-            }
-            Slog.w(TAG, "installd not ready");
-            SystemClock.sleep(1000);
-        }
-    }
-
-    public static class InstallerException extends Exception {
-        public InstallerException(String detailMessage) {
-            super(detailMessage);
-        }
-    }
-}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 32876ac..c88e9da 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -26,11 +26,16 @@
 import android.icu.util.ULocale;
 import android.net.LocalServerSocket;
 import android.opengl.EGL14;
+import android.os.IInstalld;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.ZygoteProcess;
+import android.os.storage.StorageManager;
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -43,7 +48,6 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.InstallerConnection.InstallerException;
 
 import dalvik.system.DexFile;
 import dalvik.system.PathClassLoader;
@@ -494,54 +498,55 @@
      */
     private static void performSystemServerDexOpt(String classPath) {
         final String[] classPathElements = classPath.split(":");
-        final InstallerConnection installer = new InstallerConnection();
-        installer.waitForConnection();
+        final IInstalld installd = IInstalld.Stub
+                .asInterface(ServiceManager.getService("installd"));
         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
 
-        try {
-            String sharedLibraries = "";
-            for (String classPathElement : classPathElements) {
-                // System server is fully AOTed and never profiled
-                // for profile guided compilation.
-                // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
+        String sharedLibraries = "";
+        for (String classPathElement : classPathElements) {
+            // System server is fully AOTed and never profiled
+            // for profile guided compilation.
+            // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
 
-                int dexoptNeeded;
-                try {
-                    dexoptNeeded = DexFile.getDexOptNeeded(
-                        classPathElement, instructionSet, "speed",
-                        false /* newProfile */);
-                } catch (FileNotFoundException ignored) {
-                    // Do not add to the classpath.
-                    Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
-                    continue;
-                } catch (IOException e) {
-                    // Not fully clear what to do here as we don't know the cause of the
-                    // IO exception. Add to the classpath to be conservative, but don't
-                    // attempt to compile it.
-                    Log.w(TAG, "Error checking classpath element for system server: "
-                            + classPathElement, e);
-                    dexoptNeeded = DexFile.NO_DEXOPT_NEEDED;
-                }
-
-                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                    try {
-                        installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
-                                dexoptNeeded, 0 /*dexFlags*/, "speed", null /*volumeUuid*/,
-                                sharedLibraries);
-                    } catch (InstallerException e) {
-                        // Ignore (but log), we need this on the classpath for fallback mode.
-                        Log.w(TAG, "Failed compiling classpath element for system server: "
-                                + classPathElement, e);
-                    }
-                }
-
-                if (!sharedLibraries.isEmpty()) {
-                    sharedLibraries += ":";
-                }
-                sharedLibraries += classPathElement;
+            int dexoptNeeded;
+            try {
+                dexoptNeeded = DexFile.getDexOptNeeded(
+                    classPathElement, instructionSet, "speed",
+                    false /* newProfile */);
+            } catch (FileNotFoundException ignored) {
+                // Do not add to the classpath.
+                Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
+                continue;
+            } catch (IOException e) {
+                // Not fully clear what to do here as we don't know the cause of the
+                // IO exception. Add to the classpath to be conservative, but don't
+                // attempt to compile it.
+                Log.w(TAG, "Error checking classpath element for system server: "
+                        + classPathElement, e);
+                dexoptNeeded = DexFile.NO_DEXOPT_NEEDED;
             }
-        } finally {
-            installer.disconnect();
+
+            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                final String packageName = "*";
+                final String outputPath = null;
+                final int dexFlags = 0;
+                final String compilerFilter = "speed";
+                final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
+                try {
+                    installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
+                            instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
+                            uuid, sharedLibraries);
+                } catch (RemoteException | ServiceSpecificException e) {
+                    // Ignore (but log), we need this on the classpath for fallback mode.
+                    Log.w(TAG, "Failed compiling classpath element for system server: "
+                            + classPathElement, e);
+                }
+            }
+
+            if (!sharedLibraries.isEmpty()) {
+                sharedLibraries += ":";
+            }
+            sharedLibraries += classPathElement;
         }
     }
 
diff --git a/core/java/com/android/internal/view/OneShotPreDrawListener.java b/core/java/com/android/internal/view/OneShotPreDrawListener.java
index 98ffd82..42d104a 100644
--- a/core/java/com/android/internal/view/OneShotPreDrawListener.java
+++ b/core/java/com/android/internal/view/OneShotPreDrawListener.java
@@ -25,8 +25,6 @@
  *     OneShotPreDrawListener.add(view, () -> { view.doSomething(); })
  * </code></pre>
  * <p>
- * The onPreDraw always returns true.
- * <p>
  * The listener will also remove itself from the ViewTreeObserver when the view
  * is detached from the view hierarchy. In that case, the Runnable will never be
  * executed.
@@ -36,22 +34,39 @@
     private final View mView;
     private ViewTreeObserver mViewTreeObserver;
     private final Runnable mRunnable;
+    private final boolean mReturnValue;
 
-    private OneShotPreDrawListener(View view, Runnable runnable) {
+    private OneShotPreDrawListener(View view, boolean returnValue, Runnable runnable) {
         mView = view;
         mViewTreeObserver = view.getViewTreeObserver();
         mRunnable = runnable;
+        mReturnValue = returnValue;
     }
 
     /**
-     * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
+     * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver. The
+     * return value from the OnPreDrawListener is {@code true}.
+     *
      * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
      * @param runnable The Runnable to execute in the OnPreDraw (once)
      * @return The added OneShotPreDrawListener. It can be removed prior to
      * the onPreDraw by calling {@link #removeListener()}.
      */
     public static OneShotPreDrawListener add(View view, Runnable runnable) {
-        OneShotPreDrawListener listener = new OneShotPreDrawListener(view, runnable);
+        return add(view, true, runnable);
+    }
+
+    /**
+     * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
+     *
+     * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
+     * @param returnValue The value to be returned from the OnPreDrawListener.
+     * @param runnable The Runnable to execute in the OnPreDraw (once)
+     * @return The added OneShotPreDrawListener. It can be removed prior to
+     * the onPreDraw by calling {@link #removeListener()}.
+     */
+    public static OneShotPreDrawListener add(View view, boolean returnValue, Runnable runnable) {
+        OneShotPreDrawListener listener = new OneShotPreDrawListener(view, returnValue, runnable);
         view.getViewTreeObserver().addOnPreDrawListener(listener);
         view.addOnAttachStateChangeListener(listener);
         return listener;
@@ -61,7 +76,7 @@
     public boolean onPreDraw() {
         removeListener();
         mRunnable.run();
-        return true;
+        return mReturnValue;
     }
 
     /**
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index 0bf170e..af4f777 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -20,28 +20,22 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.ActionMenuView;
 import android.widget.ForwardingListener;
 import android.widget.TextView;
-import android.widget.Toast;
 
 /**
  * @hide
  */
 public class ActionMenuItemView extends TextView
-        implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
-        ActionMenuView.ActionMenuChildView {
+        implements MenuView.ItemView, View.OnClickListener, ActionMenuView.ActionMenuChildView {
     private static final String TAG = "ActionMenuItemView";
 
     private MenuItemImpl mItemData;
@@ -59,9 +53,6 @@
     private static final int MAX_ICON_SIZE = 32; // dp
     private int mMaxIconSize;
 
-    private Toast mTooltip;
-    private Runnable mShowTooltipRunnable = () -> showTooltip(Toast.LENGTH_LONG);
-
     public ActionMenuItemView(Context context) {
         this(context, null);
     }
@@ -88,7 +79,6 @@
         mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f);
 
         setOnClickListener(this);
-        setOnLongClickListener(this);
 
         mSavedPaddingLeft = -1;
         setSaveEnabled(false);
@@ -193,6 +183,9 @@
                 (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
 
         setText(visible ? mTitle : null);
+
+        // Show the tooltip for items that do not already show text.
+        setTooltip(visible ? null : mTitle);
     }
 
     public void setIcon(Drawable icon) {
@@ -249,7 +242,6 @@
 
     @Override
     public boolean dispatchHoverEvent(MotionEvent event) {
-        updateTooltip(event);
         // Don't allow children to hover; we want this to be treated as a single component.
         return onHoverEvent(event);
     }
@@ -267,68 +259,6 @@
     }
 
     @Override
-    public boolean onLongClick(View v) {
-        return showTooltip(Toast.LENGTH_SHORT);
-    }
-
-    private boolean showTooltip(@Toast.Duration int duration) {
-        if (hasText()) {
-            // Don't show the cheat sheet for items that already show text.
-            return false;
-        }
-
-        final int[] screenPos = new int[2];
-        final Rect displayFrame = new Rect();
-        getLocationOnScreen(screenPos);
-        getWindowVisibleDisplayFrame(displayFrame);
-
-        final Context context = getContext();
-        final int width = getWidth();
-        final int height = getHeight();
-        final int midy = screenPos[1] + height / 2;
-        int referenceX = screenPos[0] + width / 2;
-        if (getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
-            final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-            referenceX = screenWidth - referenceX; // mirror
-        }
-        hideTooltip ();
-        mTooltip = Toast.makeText(context, mItemData.getTitle(), duration);
-        if (midy < displayFrame.height()) {
-            // Show along the top; follow action buttons
-            mTooltip.setGravity(Gravity.TOP | Gravity.END, referenceX,
-                    screenPos[1] + height - displayFrame.top);
-        } else {
-            // Show along the bottom center
-            mTooltip.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
-        }
-        mTooltip.show();
-        return true;
-    }
-
-    private void hideTooltip() {
-        if (mTooltip != null) {
-            mTooltip.cancel();
-            mTooltip = null;
-        }
-        getHandler().removeCallbacks(mShowTooltipRunnable);
-    }
-
-    private void updateTooltip(MotionEvent event) {
-        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
-        if (manager.isEnabled() && manager.isTouchExplorationEnabled()) {
-            return;
-        }
-
-        final int action = event.getAction();
-        if (action == MotionEvent.ACTION_HOVER_MOVE) {
-            hideTooltip();
-            getHandler().postDelayed(mShowTooltipRunnable, ViewConfiguration.getLongPressTimeout());
-        } else if (action == MotionEvent.ACTION_HOVER_EXIT) {
-            hideTooltip();
-        }
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final boolean textVisible = hasText();
         if (textVisible && mSavedPaddingLeft >= 0) {
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index 358be60..a5d2bf3 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -71,10 +71,14 @@
                 .setTextDirection(getTextDirectionHeuristic())
                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
                 .setIncludePad(getIncludeFontPadding())
-                .setEllipsize(shouldEllipsize ? effectiveEllipsize : null)
-                .setEllipsizedWidth(ellipsisWidth)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
+                .setMaxLines(getMaxLines() >= 0 ? getMaxLines() : Integer.MAX_VALUE);
+        if (shouldEllipsize) {
+            builder.setEllipsize(effectiveEllipsize)
+                    .setEllipsizedWidth(ellipsisWidth);
+        }
+
         // we set the endmargin on the requested number of lines.
         int endMargin = getContext().getResources().getDimensionPixelSize(
                 R.dimen.notification_content_picture_margin);
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index d2a43b7..cb123a1 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -126,7 +126,8 @@
                 // Pretend we need the image padding for all views, we don't know which
                 // one will end up needing to do this (might end up not using all the space,
                 // but calculating this exactly would be more expensive).
-                ((ImageFloatingTextView) child).setNumIndentLines(mIndentLines);
+                ((ImageFloatingTextView) child).setNumIndentLines(
+                        mIndentLines == 2 ? 3 : mIndentLines);
             }
 
             measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
@@ -147,6 +148,9 @@
         // Now that we know which views to take, fix up the indents and see what width we get.
         int measuredWidth = mPaddingLeft + mPaddingRight;
         int imageLines = mIndentLines;
+        // Need to redo the height because it may change due to changing indents.
+        totalHeight = mPaddingTop + mPaddingBottom;
+        first = true;
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -173,6 +177,9 @@
             measuredWidth = Math.max(measuredWidth,
                     child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
                             + mPaddingLeft + mPaddingRight);
+            totalHeight = Math.max(totalHeight, totalHeight + child.getMeasuredHeight() +
+                    lp.topMargin + lp.bottomMargin + (first ? 0 : mSpacing));
+            first = false;
         }
 
 
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index ffd9b24..4466575 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -40,7 +40,6 @@
 import android.widget.ListView;
 import android.widget.Spinner;
 import android.widget.TextView;
-import android.widget.Toast;
 
 /**
  * This widget implements the dynamic action bar tab behavior that can change
@@ -360,7 +359,7 @@
         tabView.getTab().select();
     }
 
-    private class TabView extends LinearLayout implements OnLongClickListener {
+    private class TabView extends LinearLayout {
         private ActionBar.Tab mTab;
         private TextView mTextView;
         private ImageView mIconView;
@@ -472,35 +471,10 @@
                 if (mIconView != null) {
                     mIconView.setContentDescription(tab.getContentDescription());
                 }
-
-                if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
-                    setOnLongClickListener(this);
-                } else {
-                    setOnLongClickListener(null);
-                    setLongClickable(false);
-                }
+                setTooltip(hasText? null : tab.getContentDescription());
             }
         }
 
-        public boolean onLongClick(View v) {
-            final int[] screenPos = new int[2];
-            getLocationOnScreen(screenPos);
-
-            final Context context = getContext();
-            final int width = getWidth();
-            final int height = getHeight();
-            final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-
-            Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
-                    Toast.LENGTH_SHORT);
-            // Show under the tab
-            cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
-                    (screenPos[0] + width / 2) - screenWidth / 2, height);
-
-            cheatSheet.show();
-            return true;
-        }
-
         public ActionBar.Tab getTab() {
             return mTab;
         }
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 04a7543..a31bd80 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -183,26 +183,46 @@
     return result ? JNI_TRUE : JNI_FALSE;
 }
 
+// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
+// from one to the other (though SkClipOp is destined to become a strict subset)
+static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(kDifference_SkClipOp), "");
+static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(kIntersect_SkClipOp), "");
+static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(kUnion_SkClipOp), "");
+static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(kXOR_SkClipOp), "");
+static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(kReverseDifference_SkClipOp), "");
+static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(kReplace_SkClipOp), "");
+
+static SkClipOp opHandleToClipOp(jint opHandle) {
+    // The opHandle is defined in Canvas.java to be Region::Op
+    SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
+
+    // In the future, when we no longer support the wide range of ops (e.g. Union, Xor)
+    // this function can perform a range check and throw an unsupported-exception.
+    // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw...
+
+    // Skia now takes a different type, SkClipOp, as the parameter to clipping calls
+    // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe.
+    return static_cast<SkClipOp>(rgnOp);
+}
+
 static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
                          jfloat r, jfloat b, jint opHandle) {
-    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
-    bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
+    bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
+            opHandleToClipOp(opHandle));
     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
                          jint opHandle) {
     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
-    bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, op);
+    bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
                            jint opHandle) {
     SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
-    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
-    bool nonEmptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
+    bool nonEmptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, opHandleToClipOp(opHandle));
     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
 }
 
diff --git a/core/jni/android_util_MemoryIntArray.cpp b/core/jni/android_util_MemoryIntArray.cpp
index 9513c8b..2dfbe3e 100644
--- a/core/jni/android_util_MemoryIntArray.cpp
+++ b/core/jni/android_util_MemoryIntArray.cpp
@@ -54,7 +54,7 @@
 }
 
 static jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd,
-    jboolean owner, jboolean writable)
+    jboolean owner)
 {
     if (fd < 0) {
         jniThrowException(env, "java/io/IOException", "bad file descriptor");
@@ -72,19 +72,35 @@
         return -1;
     }
 
-    int protMode = (owner || writable) ? (PROT_READ | PROT_WRITE) : PROT_READ;
+    // IMPORTANT: Ashmem allows the caller to change its size until
+    // it is memory mapped for the first time which lazily creates
+    // the underlying VFS file. So the size we get above may not
+    // reflect the size of the underlying shared memory region. Therefore,
+    // we first memory map to set the size in stone an verify if
+    // the underlying ashmem region has the same size as the one we
+    // memory mapped. This is critical as we use the underlying
+    // ashmem size for boundary checks and memory unmapping.
+    int protMode = owner ? (PROT_READ | PROT_WRITE) : PROT_READ;
     void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0);
     if (ashmemAddr == MAP_FAILED) {
         jniThrowException(env, "java/io/IOException", "cannot mmap ashmem");
         return -1;
     }
 
+    // Check if the mapped size is the same as the ashmem region.
+    int mmapedSize = ashmem_get_size_region(fd);
+    if (mmapedSize != ashmemSize) {
+        munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize);
+        jniThrowException(env, "java/io/IOException", "bad file descriptor");
+        return -1;
+    }
+
     if (owner) {
         int size = ashmemSize / sizeof(std::atomic_int);
         new (ashmemAddr) std::atomic_int[size];
     }
 
-    if (owner && !writable) {
+    if (owner) {
         int setProtResult = ashmem_set_prot_region(fd, PROT_READ);
         if (setProtResult < 0) {
             jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
@@ -131,7 +147,7 @@
 }
 
 static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz,
-        jint fd, jlong address, jint index, jboolean owner)
+        jint fd, jlong address, jint index)
 {
     if (fd < 0) {
         jniThrowException(env, "java/io/IOException", "bad file descriptor");
@@ -153,7 +169,7 @@
 }
 
 static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz,
-        jint fd, jlong address, jint index, jint newValue, jboolean owner)
+        jint fd, jlong address, jint index, jint newValue)
 {
     if (fd < 0) {
         jniThrowException(env, "java/io/IOException", "bad file descriptor");
@@ -195,10 +211,10 @@
 
 static const JNINativeMethod methods[] = {
     {"nativeCreate",  "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create},
-    {"nativeOpen",  "(IZZ)J", (void*)android_util_MemoryIntArray_open},
+    {"nativeOpen",  "(IZ)J", (void*)android_util_MemoryIntArray_open},
     {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close},
-    {"nativeGet",  "(IJIZ)I", (void*)android_util_MemoryIntArray_get},
-    {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set},
+    {"nativeGet",  "(IJI)I", (void*)android_util_MemoryIntArray_get},
+    {"nativeSet", "(IJII)V", (void*) android_util_MemoryIntArray_set},
     {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size},
 };
 
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 4b105ca..53c9ff0 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -196,7 +196,8 @@
 
     Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->setBitmap(bitmap);
-    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom);
+    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
+            kIntersect_SkClipOp);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index b6c81cf8..63997e5 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -339,7 +339,7 @@
 
     if (dirtyRectPtr) {
         nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
-                dirtyRect.right, dirtyRect.bottom);
+                dirtyRect.right, dirtyRect.bottom, kIntersect_SkClipOp);
     }
 
     if (dirtyRectObj) {
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 268aec5..351dce9 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -166,7 +166,8 @@
 
     Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->setBitmap(bitmap);
-    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom);
+    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
+            kIntersect_SkClipOp);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 95cb0d8..f9ae7cf 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Laat \'n program toe om installasiesessies te lees. Dit laat dit toe om besonderhede van aktiewe pakketinstallasies te sien."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"versoek installeerpakkette"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Laat \'n program toe om te versoek dat pakkette geïnstalleer word."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Klop twee keer vir zoembeheer"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kon nie legstuk byvoeg nie."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Gaan"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Stel nou terug"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Het <xliff:g id="LABEL">%1$s</xliff:g> gedeaktiveer"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferensie-oproep"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Nutswenk"</string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 501179d..e24ffce 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"መተግበሪያው የመጫን ክፍለ ጊዜዎችን እንዲያነብ ይፈቅድለታል። ይህም ስለ ገቢር የጥቅል ጭነቶች ዝርዝር መረጃን እንዲያይ ይፈቅድለታል።"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"የጭነት ጥቅሎችን መጠየቅ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"መተግበሪያ የጥቅሎች መጫንን እንዲጠይቅ ይፈቅዳል።"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ለአጉላ መቆጣጠሪያ ሁለት ጊዜ ነካ አድርግ"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ምግብር ማከል አልተቻለም።"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"ሂድ"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"አሁን ዳግም አስጀምር"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ተሰናክሏል"</string>
     <string name="conference_call" msgid="3751093130790472426">"የስብሰባ ጥሪ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"የመሣሪያ ጥቆማ"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index f406297..bd0a658 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -785,7 +785,7 @@
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"تأكيد الانتقال"</string>
     <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"مغادرة هذه الصفحة"</string>
     <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"البقاء في هذه الصفحة"</string>
-    <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nهل تريد بالتأكيد الانتقال بعيدًا عن هذه الصفحة؟"</string>
+    <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nهل تريد فعلًا الانتقال بعيدًا عن هذه الصفحة؟"</string>
     <string name="save_password_label" msgid="6860261758665825069">"تأكيد"</string>
     <string name="double_tap_toast" msgid="4595046515400268881">"نصيحة: اضغط مرتين للتكبير والتصغير."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"ملء تلقائي"</string>
@@ -1312,6 +1312,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"للسماح لأحد التطبيقات بقراءة جلسات التثبيت. ويسمح لك هذا بالاطلاع على تفاصيل بشأن عمليات تثبيت الحزم النشطة."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"طلب حزم التثبيت"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"للسماح لتطبيق ما بطلب تثبيت الحزم."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"اضغط مرتين للتحكم في التكبير/التصغير"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"تعذرت إضافة أداة."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"تنفيذ"</string>
@@ -1827,6 +1831,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"إعادة التعيين الآن"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"تم تعطيل <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"مكالمة جماعية"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"تلميح"</string>
 </resources>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 0f41311..6322bfa 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tətbiqə quraşdırma sessiyalarını oxumağa yardım edir. Bu da aktiv paket quraşdırmaları haqqında məlumatları görməyə imkan verir."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paketləri quraşdırma sorğusu"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Tətbiqə paketləri quraşdırma sorğusu göndərməyə icazə verir."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Zoom kontrolu üçün iki dəfə toxunun"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget əlavə edilə bilmədi."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Get"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"İndi sıfırlayın"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> deaktiv edildi"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konfrans Zəngi"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 7cbeb1a..b44b180 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1237,6 +1237,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Dozvoljava aplikaciji da čita sesije instaliranja. To joj dozvoljava da vidi detalje o aktivnim instalacijama paketa."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtevanje paketa za instaliranje"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Omogućava da aplikacija zahteva instalaciju paketa."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Dodirnite dvaput za kontrolu zumiranja"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nije moguće dodati vidžet."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Idi"</string>
@@ -1719,6 +1723,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetuj"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Vidžet <xliff:g id="LABEL">%1$s</xliff:g> je onemogućen"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Objašnjenje"</string>
 </resources>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 2299125..b035e9c 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -384,7 +384,7 @@
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"чытанне статусу тэлефона і ідэнтыфікацыя"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Дазваляе прыкладанням атрымлiваць доступ да функцый тэлефона на прыладзе. Дзякуючы гэтаму дазволу прыкладанне можа вызначаць iдэнтыфiкатары нумару тэлефона i прылады, незалежна ад таго, цi актыўны выклiк, i аддалены нумар, на якi робiцца выклiк."</string>
     <string name="permlab_readPhoneNumber" msgid="6421295519255154171">"чытаць нумар тэлефона"</string>
-    <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"Праграма зможа атрымліваць доступ да нумара тэлефона прылады."</string>
+    <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"Праграма зможа атрымліваць доступ да тэлефоннага нумара прылады."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"прадухіліць планшэт ад пераходу ў рэжым сну"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"прадухіленне пераходу тэлевізара ў рэжым сну"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"забараняць тэлефону пераходзіць ў рэжым сну"</string>
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дазваляе праграме счытваць сеансы ўсталёўкі. Гэта дазваляе ёй праглядаць інфармацыю аб актыўных усталёўках пакета."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"запытваць усталёўку пакетаў"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Дазваляе праграме запытваць усталёўку пакетаў."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Націсніце двойчы, каб кіраваць маштабаваннем"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Немагчыма дадаць віджэт."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Пачаць"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Выканаць скід"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Адключаны <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Канферэнц-выклік"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Падказка"</string>
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index aac482c..4caa906 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Разрешава на приложението да чете сесии за инсталиране. Това му позволява да вижда подробности за активните инсталирания на пакети."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"заявка на пакети за инсталиране"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Разрешава на приложението да заявява инсталиране на пакети."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Докоснете двукратно за управление на промяната на мащаба"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Приспособлението не можа да бъде добавено."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Старт"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Нулиране сега"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>: Деактивирано"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конферентно обаждане"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Подсказка"</string>
 </resources>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 99a131b..0c73ecd 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"কোনো অ্যাপ্লিকেশানকে সেশনগুলি পড়ার অনুমতি দেয়। এটি সক্রিয় প্যাকেজ ইনস্টলেশনের বিশদ বিবরণ দেখতে দেয়।"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"প্যাকেজগুলি ইনস্টল করার অনুরোধ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"একটি অ্যাপ্লিকেশানকে প্যাকেজগুলির ইনস্টল করার অনুরোধ জানাতে অনুমতি দেয়৷"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"জুম নিয়ন্ত্রণের জন্য দুবার আলতো চাপুন"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"উইজেট যোগ করা যায়নি৷"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"যান"</string>
@@ -1238,7 +1242,7 @@
     <string name="wallpaper_binding_label" msgid="1240087844304687662">"ওয়ালপেপার"</string>
     <string name="chooser_wallpaper" msgid="7873476199295190279">"ওয়ালপেপার পরিবর্তন করুন"</string>
     <string name="notification_listener_binding_label" msgid="2014162835481906429">"বিজ্ঞপ্তির শ্রোতা"</string>
-    <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR শ্রোতা"</string>
+    <string name="vr_listener_binding_label" msgid="4316591939343607306">"(ভিআর)VR শ্রোতা"</string>
     <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"শর্ত প্রদানকারী"</string>
     <string name="notification_ranker_binding_label" msgid="774540592299064747">"বিজ্ঞপ্তি র‌্যাঙ্কার পরিষেবা"</string>
     <string name="vpn_title" msgid="19615213552042827">"VPN সক্রিয়"</string>
@@ -1562,8 +1566,8 @@
     <string name="select_year" msgid="7952052866994196170">"বছর নির্বাচন করুন"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> মুছে ফেলা হয়েছে"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"কর্মক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"দ্বিতীয় কার্যক্ষেত্রের <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"তৃতীয় কার্যক্ষেত্রের <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"দ্বিতীয় কার্যক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"তৃতীয় কার্যক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="1420543809500606964">"এই স্ক্রীনটিকে আনপিন করতে, \'ফিরুন\' স্পর্শ করুন এবং ধরে রাখুন৷"</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"অ্যাপ্লিকেশান পিন করা আছে: এই ডিভাইস এটিকে পিনমুক্ত করা মঞ্জুরিপ্রাপ্ত নয়৷"</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"স্ক্রীন পিন করা হয়েছে"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"এখনই পুনরায় সেট করুন"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"অক্ষম করা <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"কনফারেন্স কল"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"সরঞ্জামটিপ"</string>
 </resources>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 0cdd4b7..f831874 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1239,6 +1239,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Dozvoljava aplikaciji da čita sesije instalacija. Ovim se aplikaciji omogućava da vidi detalje o aktivnim instalacijama paketa."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtijevanje paketa za instaliranje"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Omogućava aplikaciji da zahtijeva instalaciju paket ā."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Dodirnite dvaput za kontrolu uvećanja"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Dodavanje vidžeta nije uspjelo."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Počni"</string>
@@ -1721,6 +1725,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Vrati sada na početne postavke"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Onemogućen <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Savjet za alat"</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 339dec4..30d1706 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permet que una aplicació llegeixi les sessions d\'instal·lació i això permet veure detalls sobre les instal·lacions de paquet actives."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"sol·licitar la instal·lació de paquets"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permet que una aplicació sol·liciti la instal·lació de paquets."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Piqueu dos cops per controlar el zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"No s\'ha pogut afegir el widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Vés"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restableix ara"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> s\'ha desactivat"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferència"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Descripció emergent"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4f36487..d4ffbaa 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Povoluje aplikaci číst instalační relace. Díky tomu můžete zobrazit podrobnosti o aktivních instalacích balíčku."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"odesílání žádostí o přístup k instalačním balíčkům"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Umožňuje aplikaci požádat o instalaci balíčků."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Poklepáním můžete ovládat přiblížení"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nelze přidat."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Přejít"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetovat"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – zakázáno"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenční hovor"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Popisek"</string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 67e0ef0..815c324 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tillader, at en applikation læser installationssessioner. Dermed kan applikationen se oplysninger om aktive pakkeinstallationer."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"anmod om installation af pakker"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Tillader, at en app anmoder om installation af pakker."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tryk to gange for zoomkontrol"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget kunne ikke tilføjes."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Gå"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Nulstil nu"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – deaktiveret"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonmøde"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Værktøjstip"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 28b9748..c729160 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ermöglicht der App, Installationssitzungen zu lesen. Dadurch kann sie Details aktiver Paketinstallationen abrufen."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Installation von Paketen anfordern"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ermöglicht der App, die Installation von Paketen anzufordern"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Für Zoomeinstellung zweimal berühren"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget konnte nicht hinzugefügt werden."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Los"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Jetzt zurücksetzen"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> deaktiviert"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonkonferenz"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Kurzinfo"</string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 5612a31..4f0d2f1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Επιτρέπει σε μια εφαρμογή την ανάγνωση των περιόδων σύνδεσης εγκατάστασης. Αυτό της επιτρέπει να βλέπει λεπτομέρειες σχετικά με τις εγκαταστάσεις του ενεργού πακέτου."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ζητά πακέτα εγκατάστασης"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Επιτρέπει σε μια εφαρμογή να ζητά εγκατάσταση πακέτων."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Πατήστε δύο φορές για έλεγχο εστίασης"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Μετάβαση"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Επαναφορά τώρα"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Απενεργοποιημένο <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Κλήση συνδιάσκεψης"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Επεξήγηση εργαλείου"</string>
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index c267bc0..a85976d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Allows an application to read install sessions. This allows it to see details about active package installations."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"request install packages"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Allows an application to request installation of packages."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Go"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reset now"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c267bc0..a85976d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Allows an application to read install sessions. This allows it to see details about active package installations."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"request install packages"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Allows an application to request installation of packages."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Go"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reset now"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c267bc0..a85976d 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Allows an application to read install sessions. This allows it to see details about active package installations."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"request install packages"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Allows an application to request installation of packages."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Go"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reset now"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index cbbdb0d..19d00d0 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que una aplicación lea sesiones de instalación. Esto le permite ver detalles acerca de instalaciones de paquetes activas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar la instalación de paquetes"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que una aplicación solicite la instalación de paquetes."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Presiona dos veces para obtener el control del zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"No se pudo agregar el widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restablecer ahora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Se inhabilitó <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferencia"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Información sobre la herramienta"</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c3751bd..487cf74 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que una aplicación consulte sesiones de instalación para ver detalles sobre instalaciones de paquetes activos."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a una aplicación solicitar la instalación de paquetes."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Da dos toques para acceder al control de zoom."</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"No se ha podido añadir el widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restablecer ahora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> inhabilitado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferencia"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Descripción emergente"</string>
 </resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 648bd62..7f46309 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Lubab rakendusel lugeda installiseansse. See võimaldab näha aktiivse paketi installimise üksikasju."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"installipakettide taotlemine"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Võimaldab rakendusel pakettide installimist taotleda."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Suumi kasutamiseks koputage kaks korda"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidinat ei saanud lisada."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Mine"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Lähtesta kohe"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Keelatud <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konverentskõne"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Tööriistavihje"</string>
 </resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 5ee9843..2490b21 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Instalazio-saioak irakurtzea baimentzen die aplikazioei. Horrela, pakete-instalazio aktiboei buruzko xehetasunak ikus ditzakete."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Eskatu instalazio-paketeak"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Paketeak instalatzeko eskatzea baimentzen die aplikazioei."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Sakatu birritan zooma kontrolatzeko"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ezin izan da widgeta gehitu."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Joan"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Berrezarri"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> desgaituta dago"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferentzia-deia"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Aholkua"</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f10fa72..63c0d2a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"به برنامه اجازه می‌دهد جلسات نصب را بخواند. این کار به برنامه اجازه می‌دهد جزئیات نصب‌های بسته فعال را ببیند."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"درخواست نصب بسته"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"به برنامه اجازه می‌دهد درخواست نصب بسته‌بندی کند."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"برای کنترل بزرگ‌نمایی، دو بار ضربه بزنید"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"افزودن ابزارک انجام نشد."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"برو"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"بازنشانی در این لحظه"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> غیرفعال شد"</string>
     <string name="conference_call" msgid="3751093130790472426">"تماس کنفرانسی"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"نکته‌ابزار"</string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 05fe294..9e9d3d1 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Sallii sovelluksen lukea asennusistuntoja. Toiminto sallii sovelluksen lukea aktiivisten asennuspakettien tietoja."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"pyytää asennuspaketteja"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Antaa sovelluksen pyytää pakettien asennusta."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Hallitse zoomausta napauttamalla kahdesti"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widgetin lisääminen epäonnistui."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Siirry"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Palauta nyt"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ei ole käytössä."</string>
     <string name="conference_call" msgid="3751093130790472426">"Puhelinneuvottelu"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Työkaluvinkki"</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 1ab43ff..15aaf84 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permet à une application d\'accéder aux sessions d\'installation. Cela lui permet de consulter les détails relatifs à l\'installation des paquets actifs."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"demander l\'installation de paquets"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permet à une application de demander l\'installation de paquets."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Appuyer deux fois pour régler le zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Impossible d\'ajouter le widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Aller"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Réinitialiser maintenant"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Désactivé : <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conférence téléphonique"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Infobulle"</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index bdd52c6..b8fe1e0 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permet à une application d\'accéder aux sessions d\'installation. Cela lui permet de consulter les détails relatifs à l\'installation des packages actifs."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"demander l\'installation de packages"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permet à une application de demander l\'installation de packages."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Appuyer deux fois pour régler le zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Impossible d\'ajouter le widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"OK"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Réinitialiser maintenant"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Élément \"<xliff:g id="LABEL">%1$s</xliff:g>\" désactivé"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conférence téléphonique"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Info-bulle"</string>
 </resources>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index b6095eb..b3fbfe8 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que unha aplicación consulte as sesións de instalación. Desta forma, pode ver os detalles acerca das instalacións de paquetes activas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a unha aplicación solicitar a instalación dos paquetes."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Toca dúas veces para controlar o zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Non se puido engadir o widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1562,8 +1566,8 @@
     <string name="select_year" msgid="7952052866994196170">"Seleccionar ano"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> eliminado"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> do traballo"</string>
-    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> do segundo traballo"</string>
-    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> do terceiro traballo"</string>
+    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2.º <xliff:g id="LABEL">%1$s</xliff:g> do traballo"</string>
+    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3.º <xliff:g id="LABEL">%1$s</xliff:g> do traballo"</string>
     <string name="lock_to_app_toast" msgid="1420543809500606964">"Para soltar a pantalla, mantén premido Volver."</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"A aplicación está fixada: non se permite soltala neste dispositivo."</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"Pantalla fixada"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Restablecer agora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Desactivouse <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferencia telefónica"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Cadro de información"</string>
 </resources>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index bde7708..d8578c9 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"એપ્લિકેશનને ઇન્સ્ટોલ સત્રોને વાંચવાની મંજૂરી આપે છે. આ તેને સક્રિય પૅકેજ ઇન્સ્ટોલેશન્સ વિશે વિગતો જોવાની મંજૂરી આપે છે."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"પૅકેજેસ ઇન્સ્ટૉલ કરવાની વિનંતી કરો"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"એપ્લિકેશનને પૅકેજેસના ઇન્સ્ટોલેશનની વિનંતી કરવાની મંજૂરી આપો."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ઝૂમ નિયંત્રણ માટે બેવાર ટૅપ કરો"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"વિજેટ ઉમેરી શકાયું નથી."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"જાઓ"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"હમણાં ફરીથી સેટ કરો"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> અક્ષમ કર્યું"</string>
     <string name="conference_call" msgid="3751093130790472426">"કોન્ફરન્સ કૉલ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ટૂલટિપ"</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index cb972db..c2f35f9 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ऐप्लिकेशन को इंस्टॉल सत्रों को पढ़ने देती है. इससे उसे सक्रिय पैकेज इंस्टॉलेशन के बारे में विवरण देखने की अनुमति मिल जाती है."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"पैकेज इंस्टॉल करने का अनुरोध करें"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"किसी ऐप्लिकेशन को पैकेज इंस्टॉल करने के अनुरोध की अनुमति देता है."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ज़ूम नियंत्रण के लिए दो बार टैप करें"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट नहीं जोड़ा जा सका."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"जाएं"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"अभी रीसेट करें"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"अक्षम <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"कॉन्फ़्रेंस कॉल"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"टूलटिप"</string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index ca8ef72..7106e35 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1237,6 +1237,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Omogućuje aplikaciji čitanje sesija instaliranja. Aplikacija može vidjeti pojedinosti o aktivnim instaliranjima paketa."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtijevati instaliranje paketa"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Aplikaciji omogućuje zahtijevanje instaliranja paketa."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Dvaput dotaknite za upravljanje zumiranjem"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget nije moguće dodati."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Idi"</string>
@@ -1719,6 +1723,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Vrati na zadano sada"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – onemogućeno"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Opis"</string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index d2211d8..6acbcb6 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Engedélyezi az alkalmazásnak a telepítési munkamenetek olvasását. Ezáltal részleteket kaphat az egyes csomagok éppen folyamatban lévő telepítéséről."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"telepítőcsomagok kérése"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lehetővé teszi az alkalmazás számára csomagok telepítésének kérését."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Érintse meg kétszer a nagyítás beállításához"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nem sikerült hozzáadni a modult."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ugrás"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Visszaállítás most"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"A(z) <xliff:g id="LABEL">%1$s</xliff:g> letiltva"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenciahívás"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Elemleírás"</string>
 </resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index d309ce4..85fb492 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -600,7 +600,7 @@
     <string name="phoneTypeOther" msgid="1544425847868765990">"Այլ"</string>
     <string name="phoneTypeCallback" msgid="2712175203065678206">"Ետզանգ"</string>
     <string name="phoneTypeCar" msgid="8738360689616716982">"Մեքենա"</string>
-    <string name="phoneTypeCompanyMain" msgid="540434356461478916">"Ընկերության գլխավոր"</string>
+    <string name="phoneTypeCompanyMain" msgid="540434356461478916">"Ընկերության հիմնական"</string>
     <string name="phoneTypeIsdn" msgid="8022453193171370337">"ISDN"</string>
     <string name="phoneTypeMain" msgid="6766137010628326916">"Հիմնական"</string>
     <string name="phoneTypeOtherFax" msgid="8587657145072446565">"Այլ ֆաքս"</string>
@@ -645,14 +645,14 @@
     <string name="relationTypeAssistant" msgid="6274334825195379076">"Օգնական"</string>
     <string name="relationTypeBrother" msgid="8757913506784067713">"Եղբայր"</string>
     <string name="relationTypeChild" msgid="1890746277276881626">"Երեխա"</string>
-    <string name="relationTypeDomesticPartner" msgid="6904807112121122133">"Տեղական գործընկեր"</string>
+    <string name="relationTypeDomesticPartner" msgid="6904807112121122133">"Կենակից"</string>
     <string name="relationTypeFather" msgid="5228034687082050725">"Հայր"</string>
     <string name="relationTypeFriend" msgid="7313106762483391262">"Ընկեր"</string>
     <string name="relationTypeManager" msgid="6365677861610137895">"Կառավարիչ"</string>
     <string name="relationTypeMother" msgid="4578571352962758304">"Մայր"</string>
     <string name="relationTypeParent" msgid="4755635567562925226">"Ծնող"</string>
     <string name="relationTypePartner" msgid="7266490285120262781">"Գործընկեր"</string>
-    <string name="relationTypeReferredBy" msgid="101573059844135524">"Հղված է"</string>
+    <string name="relationTypeReferredBy" msgid="101573059844135524">"Հանձնարարված"</string>
     <string name="relationTypeRelative" msgid="1799819930085610271">"Բարեկամ"</string>
     <string name="relationTypeSister" msgid="1735983554479076481">"Քույր"</string>
     <string name="relationTypeSpouse" msgid="394136939428698117">"Ամուսին"</string>
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ծրագրին թույլ է տալիս կարդալ տեղադրման աշխատաշրջանները: Սա թույլ է տալիս տեղեկանալ փաթեթների ակտիվ տեղադրումների մանրամասներին:"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"պահանջել տեղադրման փաթեթներ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Թույլ է տալիս հավելվածին պահանջել փաթեթների տեղադրումը:"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Հպեք երկու անգամ` խոշորացման վերահսկման համար"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Չհաջողվեց վիջեթ ավելացնել:"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Առաջ"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Վերակայել հիմա"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Անջատած <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Կոնֆերանս զանգ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Հուշակ"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index ae034c1..343191c 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Memungkinkan aplikasi membaca sesi pemasangan. Tindakan ini memungkinkannya melihat detail tentang pemasangan paket aktif."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"minta pasang paket"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Mengizinkan aplikasi meminta pemasangan paket."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Ketuk dua kali untuk kontrol perbesar/perkecil"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Buka"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Setel ulang sekarang"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> dinonaktifkan"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferensi Telepon"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Keterangan alat"</string>
 </resources>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 6154e12..a875301 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Leyfir forriti að lesa uppsetningarlotur. Þetta gerir því kleift að sjá upplýsingar um virkar pakkauppsetningar."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"fara fram á uppsetningu pakka"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Leyfir forriti að fara fram á uppsetningu pakka."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Ýttu tvisvar til að opna aðdráttarstýringar"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Ekki tókst að bæta græju við."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Áfram"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Endurstilla núna"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Slökkt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Símafundur"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Ábending"</string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4e9a3bf..7bae091 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Consente a un\'applicazione di leggere le sessioni di installazione. L\'app può conoscere i dettagli sulle installazioni di pacchetti attive."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"richiesta di pacchetti di installazione"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Consente a un\'applicazione di richiedere l\'installazione di pacchetti."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tocca due volte per il comando dello zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Aggiunta del widget non riuscita."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Vai"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ripristina ora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> disattivato"</string>
     <string name="conference_call" msgid="3751093130790472426">"Audioconferenza"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Descrizione comando"</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 427832c..c79f889 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"מאפשר לאפליקציה לקרוא הפעלות התקנה. הרשאה זו מאפשרת לה לראות פרטים על התקנות פעילות של חבילות."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"בקשה להתקנת חבילות"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"מתיר לאפליקציה לבקש התקנה של חבילות."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"הקש פעמיים לבקרת מרחק מתצוגה"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"‏לא ניתן להוסיף widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"התחל"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"אפס עכשיו"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> הושבת"</string>
     <string name="conference_call" msgid="3751093130790472426">"שיחת ועידה"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"הסבר קצר"</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2f09977..f2e927e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"インストールセッションの読み取りをアプリに許可します。これにより、アプリはアクティブパッケージのインストールに関する詳細情報を参照できるようになります。"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"インストールパッケージのリクエスト"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"パッケージのインストールをリクエストすることをアプリケーションに許可します。"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ダブルタップでズームします"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ウィジェットを追加できませんでした。"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"移動"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"今すぐリセット"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"停止済みの「<xliff:g id="LABEL">%1$s</xliff:g>」"</string>
     <string name="conference_call" msgid="3751093130790472426">"グループ通話"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ツールチップ"</string>
 </resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 1686591..dfdc182c 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"საშუალებას აძლევს აპლიკაციას წაიკითხოს ინსტალაციის სესიები. ამით მას საშუალება აქვს იხილოს პაკეტის აქტიური ინსტალაციები."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"პაკეტების ინსტალაციის მოთხოვნა"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"აპლიკაციას შეეძლება მოითხოვოს პაკეტების ინსტალაცია."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"მასშტაბის ცვლილებისთვის შეეხეთ ორჯერ"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ვერ დაემატა ვიჯეტი."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"გადასვლა"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ახლავე გადაყენება"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"გათიშული <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"საკონფერენციო ზარი"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"მინიშნება"</string>
 </resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 210fd01..bd4e171 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Қолданбаға орнату сеанстарын оқуға рұқсат етеді. Бұл оған белсенді бума орнатулары туралы мәліметтерді көруге рұқсат етеді."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнату бумаларын сұрау"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Қолданбаның бумаларды орнатуға рұқсат сұрауына мүмкіндік береді."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Масштабтау параметрін басқару үшін екі рет түртіңіз"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетті қосу."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Өту"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Қазір бастапқы күйге қайтару"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> өшірулі"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференциялық қоңырау"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Қалқыма сөзкөмек"</string>
 </resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 04e7681..49d3a64 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1214,6 +1214,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ឲ្យ​កម្មវិធី​អាន​សម័យ​ដំឡើង។ វា​អនុញ្ញាត​ឲ្យ​ឃើញ​ព័ត៌មាន​លម្អិត​អំពី​​ការដំឡើង​កញ្ចប់​សកម្ម។"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ស្នើសុំកញ្ចប់ដំឡើង"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំដំឡើងកញ្ចប់ (ឯកសារ/មាតិកា)។"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ប៉ះ ពីរ​ដង​ដើម្បី​ពិនិត្យ​ការ​ពង្រីក"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"មិន​អាច​បន្ថែម​ធាតុ​ក្រាហ្វិក។"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
@@ -1685,6 +1689,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"កំណត់ឡើងវិញឥឡូវនេះ"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ដែលបានបិទដំណើរការ"</string>
     <string name="conference_call" msgid="3751093130790472426">"ការហៅជាក្រុម"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ផ្ទាំងលោត"</string>
 </resources>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 21cf1ba..bfd69d6 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ಸ್ಥಾಪಿತ ಸೆಷನ್‌ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ. ಸಕ್ರಿಯ ಪ್ಯಾಕೇಜ್‌ ಸ್ಥಾಪನೆಗಳ ಕುರಿತು ವಿವರಣೆಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಇದು ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ಸ್ಥಾಪನೆ ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ವಿನಂತಿಸಿ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ಪ್ಯಾಕೇಜ್‌ಗಳ ಸ್ಥಾಪನೆಯನ್ನು ವಿನಂತಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ಝೂಮ್‌ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"ಹೋಗು"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ಈಗಲೇ ಮರುಹೊಂದಿಸು"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="conference_call" msgid="3751093130790472426">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ಟೂಲ್‌ಟಿಪ್"</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 70388bf..e81de03 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"애플리케이션의 설치 세션 읽기를 허용하면, 활성 패키지 설치에 대한 세부 정보를 볼 수 있습니다."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"패키지 설치 요청"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"애플리케이션이 패키지 설치를 요청하도록 허용합니다."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"확대/축소하려면 두 번 탭하세요."</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"위젯을 추가할 수 없습니다."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"이동"</string>
@@ -1562,8 +1566,8 @@
     <string name="select_year" msgid="7952052866994196170">"연도 선택"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> 삭제됨"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"업무용 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"두번째 작업 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"세번째 작업<xliff:g id="LABEL">%1$s</xliff:g>"</string>
+    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"두 번째 업무용 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"세 번째 업무용<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="1420543809500606964">"이 화면을 고정 해제하려면 \'뒤로\'를 길게 터치합니다."</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"앱이 고정되었습니다. 이 기기에서는 고정 해제를 허용하지 않습니다."</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"화면 고정됨"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"지금 초기화"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> 사용 중지됨"</string>
     <string name="conference_call" msgid="3751093130790472426">"다자간 통화"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"도움말"</string>
 </resources>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 0c799f5..d4fa9d8 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Колдонмого орнотуу сеанстарын окуу мүмкүнчүлүгүн берет. Ушуну менен, ал жигердүү топтом орнотууларынын чоо-жайын көрө алат."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнотуу топтомдорун суроо"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Колдонмо топтомдорду орнотууга уруксат сурай алат."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Масштабдын параметрлерин өзгөртүү үчүн бул жерди эки жолу басыңыз."</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетти кошуу мүмкүн болбоду."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Өтүү"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Баштапкы абалга келтирүү"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> өчүрүлдү"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференц чалуу"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Калкып чыгуучу кеңеш"</string>
 </resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 9e91888..7e0f79c 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"​ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ພລິ​ເຄ​ຊັນ​ອ່ານ​ເຊດ​ຊັນ​ການ​ຕິດ​ຕັ້ງ​ໄດ້. ນີ້​ຈະ​ອະ​ນຸ​ຍາດ​ໃຫ້​ມັນ​ເບິ່ງ​ເຫັນ​ລາຍ​ລະ​ອຽດ​ກ່ຽວ​ກັບ​ການ​ຕິດ​ຕັ້ງ​ແພັກ​ເກດ​ທີ່​ເຮັດ​​ວຽກ​ຢູ່​ໄດ້."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ຂໍ​ຕິດ​ຕັ້ງ​ແພັກ​ເກດ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ພ​ລິ​ເຄ​ຊັນ​ຂອງ​ການ​ຕິດ​ຕັ້ງ​ແພັກ​ເກດ."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ແຕະສອງເທື່ອເພື່ອຄວບຄຸມການຊູມ"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ບໍ່ສາມາດເພີ່ມວິດເຈັດໄດ້."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"ໄປ"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ຣີເຊັດດຽວນີ້"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ປິດການນຳໃຊ້ <xliff:g id="LABEL">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="conference_call" msgid="3751093130790472426">"ການປະຊຸມສາຍ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ຄຳອະທິບາຍເຄື່ອງມື"</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e4f3553..3100570 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Leidžiama programai skaityti diegimo seansus. Leidžiama peržiūrėti išsamią aktyvių paketų diegimo informaciją."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"pateikti užklausą dėl diegimo paketų"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Programai leidžiama pateikti užklausą dėl paketų diegimo."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Bakstelėkite du kartus, kad valdytumėte mastelio keitimą"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nepavyko pridėti."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Pradėti"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Nustatyti iš naujo dabar"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Išj. valdiklis „<xliff:g id="LABEL">%1$s</xliff:g>“"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencinis skambutis"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Patarimas"</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 1b9fdbc..5393e49 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1237,6 +1237,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ļauj lietojumprogrammai lasīt instalēšanas sesijas. Tādējādi lietojumprogrammai ir pieejama informācija par aktīvajām pakotņu instalācijām."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Pieprasīt pakotņu instalēšanu"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ļauj lietojumprogrammai pieprasīt pakotņu instalēšanu."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nevarēja pievienot logrīku."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Doties uz"</string>
@@ -1719,6 +1723,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Atiestatīt tūlīt"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> atspējots"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferences zvans"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Rīka padoms"</string>
 </resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 7a32076..f0c9a8d 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -377,7 +377,7 @@
     <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Дозволува апликацијата да ја користи услугата IMS за повици без ваша интервенција."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"прочитај ги статусот и идентитетот  на телефонот"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Овозможува апликацијата да пристапи кон карактеристиките на телефонот на уредот. Оваа дозвола овозможува апликацијата да ги утврди телефонскиот број и ID на уредот, дали повикот е активен и далечинскиот број поврзан со повикот."</string>
-    <string name="permlab_readPhoneNumber" msgid="6421295519255154171">"прочитај телефонски број"</string>
+    <string name="permlab_readPhoneNumber" msgid="6421295519255154171">"да го чита телефонскиот број"</string>
     <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"Ѝ дозволува на апликацијата да пристапи до телефонскиот број на уредот."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"спречи режим на штедење кај таблет"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"спречи го телевизорот да премине во режим на мирување"</string>
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозволува апликација да чита сесии на инсталирање. Тоа овозможува апликацијата да гледа детали за активни инсталации на пакет."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"барање пакети за инсталирање"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Дозволува апликацијата да бара инсталација на пакети."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Допрете двапати за контрола на зумот"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не можеше да се додаде виџет."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Оди"</string>
@@ -1685,6 +1689,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ресетирај сега"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Оневозможен <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференциски повик"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Совет за алатка"</string>
 </resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 946ac13..a1e7214 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ഇൻസ്റ്റാൾ ചെയ്‌ത സെഷനുകൾ റീഡുചെയ്യുന്നതിന് ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. സജീവ പാക്കേജ് ഇൻസ്റ്റാളേഷനുകളെക്കുറിച്ചുള്ള വിശദാംശങ്ങൾ കാണുന്നതിന് ഇത് അനുവദിക്കുന്നു."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"പാക്കേജുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ അഭ്യർത്ഥിക്കുക"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"പാക്കേജുകളുടെ ഇൻസ്റ്റാളേഷൻ അഭ്യർത്ഥിക്കാൻ ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"പോവുക"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ഇപ്പോൾ പുനക്രമീകരിക്കുക"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="conference_call" msgid="3751093130790472426">"കോൺഫറൻസ് കോൾ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ടൂൾ ടിപ്പ്"</string>
 </resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 50333a8..3f9618c 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Аппликешн-д суулгах сешн уншихыг зөвшөөрнө. Энэ нь идэвхтэй багцуудыг суулгалтын талаар дэлгэрэнгүй мэдээллийг үзэх боломж олгоно."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"багц суулгахыг хүсэх"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Аппликейшн нь багц суулгахыг хүсэх боломжтой."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Өсгөх контрол дээр хоёр удаа товшино уу"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджет нэмж чадсангүй."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Очих"</string>
@@ -1681,6 +1685,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Одоо шинэчлэх"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>-г цуцалсан"</string>
     <string name="conference_call" msgid="3751093130790472426">"Хурлын дуудлага"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Зөвлөмж"</string>
 </resources>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index bee5dd5..d5487e1 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"अनुप्रयोगास स्‍थापना सत्र वाचण्‍याची अनुमती देते. हे सक्रिय पॅकेज स्‍थापनांविषयी तपशील पाहाण्‍याची यास अनुमती देते."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"पॅकेज स्थापित करण्यासाठी विनंती करा"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"पॅकेजच्या स्थापना करण्यासाठी अनुप्रयोगास अनुमती देते."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"झूम नियंत्रणासाठी दोनदा टॅप करा"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट जोडू शकलो नाही."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"जा"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"आता रीसेट करा"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string>
     <string name="conference_call" msgid="3751093130790472426">"परिषद कॉल"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"टूलटिप"</string>
 </resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 5dcff04..1ef6493 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Membenarkan aplikasi membaca sesi pemasangan Ini membenarkan apl melihat butiran mengenai pemasangan pakej yang aktif."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"minta pakej pemasangan"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Membenarkan aplikasi meminta pemasangan pakej."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Ketik dua kali untuk mendapatkan kawalan zum"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Tidak dapat menambahkan widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Pergi"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Tetapkan semula sekarang"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> dilumpuhkan"</string>
     <string name="conference_call" msgid="3751093130790472426">"Panggilan Sidang"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Keterangan item"</string>
 </resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 086a2ef..2117c56 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -378,7 +378,7 @@
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"ဖုန်းရဲ့ အခြေအနေ နှင့် အမှတ်သညာအား ဖတ်ခြင်း"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"အပလီကေးရှင်းအား ဖုန်းရဲ့ စွမ်းဆောင်ချက်များအား သုံးခွင့်ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် ဖုန်းနံပါတ်၊ စက်နံပါတ်၊ ဖုန်းခေါ်နေမှု ရှိမရှိနှင့် တဖက်မှ ဖုန်းနံပါတ် များအား သိရှိနိုင်ပါသည်"</string>
     <string name="permlab_readPhoneNumber" msgid="6421295519255154171">"ဖုန်းနံပါတ်ကို ဖတ်ရန်"</string>
-    <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"စက်ပစ္စည်း၏ ဖုန်းနံပါတ်အား အက်ပ်ကို အသုံးပြုခွင့်ပေးပါ။"</string>
+    <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"အက်ပ်ကို စက်ပစ္စည်း၏ ဖုန်းနံပါတ် အသုံးပြုခွင့်ပေးပါ။"</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"တက်ပလက်အား ပိတ်ခြင်းမှ ကာကွယ်ခြင်း"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"တီဗွီအား နားနေခြင်းမှ ကာကွယ်ရန်"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"ဖုန်းအနားယူခြင်းမပြုလုပ်စေရန်"</string>
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"အပလီကေးရှင်းအား တပ်ဆင်ရေး ချိတ်ဆက်မှုများကို ဖတ်ခွင့်ပြုသည်။ ၎င်းသည် ဖွင့်သုံးနေသည့် အထုပ်အား တပ်ဆင်မှုဆိုင်ရာ အသေးိစတ်များကို ကြည့်ရှုခွင့် ပြုသည်။"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"တပ်ဆင်ရေး အထုပ်များကို တောင်းဆိုပါ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ပက်ကေ့များ သွင်းယူခြင်းအတွက် တောင်းဆိုရန် အပလီကေးရှင်းအား ခွင့်ပြုပါ"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ဇူးမ်အသုံးပြုရန် နှစ်ချက်တို့ပါ"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ဝဒ်ဂျက်ထည့်လို့ မရပါ"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"သွားပါ"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ယခုပြန်လည်သတ်မှတ်ပါ"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ပိတ်ထားသည့် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"လူအမြောက်အမြားတပြိုင်နက် ခေါ်ဆိုမှု"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"အကြံပြုချက်ပြ ပေါ့အပ် ဝင်းဒိုး"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index cc3232a..f2b11c9 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tillater en app å lese installeringsøkter. Dette gjør det mulig for den å se detaljer om aktive pakkeinstallasjoner."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"be om installasjon av pakker"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lar apper be om installasjon av pakker."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Trykk to ganger for zoomkontroll"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kunne ikke legge til modulen."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Utfør"</string>
@@ -1562,8 +1566,8 @@
     <string name="select_year" msgid="7952052866994196170">"Velg året"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> er slettet"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Jobb-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Andre jobbrelaterte <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Tredje jobbrelaterte <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+    <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Andre <xliff:g id="LABEL">%1$s</xliff:g> for jobben"</string>
+    <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Tredje <xliff:g id="LABEL">%1$s</xliff:g> for jobben"</string>
     <string name="lock_to_app_toast" msgid="1420543809500606964">"For å løsne denne skjermen, trykk og hold inne Tilbake."</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"Appen er festet – du kan ikke løsne apper på denne enheten."</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"Skjermen er festet"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Tilbakestill nå"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> er slått av"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferansesamtale"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Verktøytips"</string>
 </resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 1517b08..15f787937 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -1218,6 +1218,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"स्थापित सत्र पढ्न अनुप्रयोगलाई अनुमति दिनुहोस्। यसले सक्रिय प्याकेज प्रतिष्ठानहरू बारेमा विवरण हेर्ने अनुमति दिन्छ।"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"स्थापना प्याकेजहरू अनुरोध गर्नुहोस्"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"प्याकेजहरूको स्थापना अनुरोध गर्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"जुम नियन्त्रणको लागि दुई चोटि ट्याप गर्नुहोस्"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"विजेट थप गर्न सकिँदैन।"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"जानुहोस्"</string>
@@ -1689,6 +1693,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"अहिले रिसेट गर्नुहोस्"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> लाई असक्षम गरियो"</string>
     <string name="conference_call" msgid="3751093130790472426">"सम्मेलन कल"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"उपकरणको वर्णन"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 652021d..9112a8c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Hiermee wordt een app toegestaan installatiesessies te lezen. Zo kan de app informatie bekijken over actieve pakketinstallaties."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"installatiepakketten aanvragen"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Hiermee kan een app installatie van pakketten aanvragen."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tik twee keer voor zoomregeling"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Kan widget niet toevoegen."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ga"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Nu resetten"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> uitgeschakeld"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonische vergadering"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Knopinfo"</string>
 </resources>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 4e95358b..a3628cc 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -378,7 +378,7 @@
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"ਫੋਨ ਸਥਿਤੀ ਅਤੇ ਪਛਾਣ ਪੜ੍ਹੋ"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੀਆਂ ਫੋਨ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਫ਼ੋਨ ਨੰਬਰ ਅਤੇ ਡੀਵਾਈਸ ID ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ, ਇੱਕ ਕਾਲ ਸਕਿਰਿਆ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਰਿਮੋਟ ਨੰਬਰ ਇੱਕ ਕਾਲ ਨਾਲ ਕਨੈਕਟ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
     <string name="permlab_readPhoneNumber" msgid="6421295519255154171">"ਫ਼ੋਨ ਨੰਬਰ ਪੜ੍ਹੋ"</string>
-    <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"ਡੀਵਾਈਸ ਦੇ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਲਈ ਐਪ ਨੂੰ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ।"</string>
+    <string name="permdesc_readPhoneNumber" msgid="9135856402838173711">"ਡੀਵਾਈਸ ਦੇ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਲਈ ਐਪ ਨੂੰ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"ਟੈਬਲੇਟ ਨੂੰ ਸਲੀਪਿੰਗ ਤੋਂ ਰੋਕੋ"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"TV ਨੂੰ ਸਲੀਪਿੰਗ ਤੋਂ ਰੋਕੋ"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"ਫੋਨ ਨੂੰ ਸਲੀਪਿੰਗ ਤੋਂ ਰੋਕੋ"</string>
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ਇੱਕ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਇੰਸਟੌਲ ਸੈਸ਼ਨ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਸਨੂੰ ਸਕਿਰਿਆ ਪੈਕੇਜ ਇੰਸਟੌਲੇਸ਼ਨਾਂ ਬਾਰੇ ਵੇਰਵੇ ਦੇਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ਪੈਕੇਜ ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕਰੋ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ਪੈਕੇਜ ਦੀ ਸਥਾਪਨਾ ਦੀ ਬੇਨਤੀ ਕਰਨ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਅਨੁਮਤੀ ਦਿੰਦਾ ਹੈ"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"ਜ਼ੂਮ ਕੰਟਰੋਲ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ਵਿਜੇਟ ਨਹੀਂ ਜੋੜ ਸਕਿਆ।"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"ਜਾਓ"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ਹੁਣੇ ਮੁੜ-ਸੈੱਟ ਕਰੋ"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ਟੂਲਟਿਪ"</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 3217af5..bdd601a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Pozwala aplikacji odczytywać sesje instalacji. Umożliwia to jej na poznanie szczegółów aktywnych instalacji pakietów."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"żądanie instalacji pakietów"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Zezwala aplikacji żądanie instalacji pakietów."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Dotknij dwukrotnie, aby sterować powiększeniem"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nie można dodać widżetu."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"OK"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetuj teraz"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Wyłączono: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Połączenie konferencyjne"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Etykietka"</string>
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 9631684..40bb94a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que um app leia sessões de instalação. Isso permite que ele veja detalhes sobre as instalações de pacote ativas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar pacotes de instalação"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que um app solicite a instalação de pacotes."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Toque duas vezes para ter controle do zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reiniciar agora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Teleconferência"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Dica"</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4c07dfa7..6fdaa19 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que uma aplicação leia sessões de instalação. Isto permite que veja detalhes acerca de instalações de pacotes ativas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar pacotes de instalação"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que uma aplicação solicite a instalação de pacotes."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tocar duas vezes para controlar o zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Repor agora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferência"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Sugestão"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 9631684..40bb94a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que um app leia sessões de instalação. Isso permite que ele veja detalhes sobre as instalações de pacote ativas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar pacotes de instalação"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite que um app solicite a instalação de pacotes."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Toque duas vezes para ter controle do zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Não foi possível adicionar widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Reiniciar agora"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Teleconferência"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Dica"</string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index c1f1d7b..e732022 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1237,6 +1237,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite unei aplicații accesul la citirea sesiunilor de instalare. Aceasta poate vedea detalii despre instalările de pachete active."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"să solicite pachete de instalare"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite unei aplicații să solicite instalarea pachetelor."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nu s-a putut adăuga widgetul."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Accesați"</string>
@@ -1719,6 +1723,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetați acum"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> a fost dezactivat"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferință telefonică"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Balon explicativ"</string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0d51926..9c45d89 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -90,7 +90,7 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Идентификация абонента по умолчанию не запрещена. След. вызов: разрешена"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Услуга не предоставляется."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"Невозможно изменить параметр идентификатора вызывающего абонента."</string>
-    <string name="RestrictedOnData" msgid="8653794784690065540">"Служба данных заблокирована."</string>
+    <string name="RestrictedOnData" msgid="8653794784690065540">"Передача данных заблокирована."</string>
     <string name="RestrictedOnEmergency" msgid="6581163779072833665">"Служба экстренной помощи заблокирована."</string>
     <string name="RestrictedOnNormal" msgid="4953867011389750673">"Служба передачи голосовых сообщений заблокирована."</string>
     <string name="RestrictedOnAllVoice" msgid="3396963652108151260">"Все службы передачи голосовых сообщений заблокированы."</string>
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Чтение данных текущих сеансов установки пакетов."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Запрос пакетов установки"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Приложение сможет запрашивать разрешения на установку пакетов."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Нажмите дважды для изменения масштаба"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не удалось добавить виджет."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Выбрать"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Сбросить"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Виджет <xliff:g id="LABEL">%1$s</xliff:g> отключен"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференц-связь"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Подсказка"</string>
 </resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index ecf6eaf..87a277b 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -1214,6 +1214,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ස්ථාපන සැසිය කියවීමට යෙදුමට ඉඩ දෙන්න. සක්‍රිය පැකේජ ස්ථාපනය පිළිබඳ විස්තර බැලීමට එයට මෙයින් ඉඩ දෙයි."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ස්ථාපන පැකේජ ඉල්ලීම"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ස්ථාපන පැකේජ ඉල්ලීමට යෙදුමකට අවසර දීම."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"විශාලන පාලක සඳහා දෙවතාවක් තට්ටු කරන්න"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"විජටය එකතු කිරීමට නොහැකි විය."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"යන්න"</string>
@@ -1685,6 +1689,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"දැන් යළි සකසන්න"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"අබල කළ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"සම්මන්ත්‍රණ ඇමතුම"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"මෙවලම් ඉඟිය"</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index d7ccda9..7811a46 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Toto povolenie umožňuje aplikácii čítať relácie inštalácií a zobraziť tak podrobnosti o aktívnych inštaláciách balíkov."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"odosielanie žiadostí o inštaláciu balíkov"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Umožňuje aplikácii vyžiadať inštaláciu balíkov."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Dvojitým klepnutím môžete ovládať priblíženie"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Miniaplikáciu sa nepodarilo pridať."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Hľadať"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Resetovať"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Deaktivovaná miniaplikácia <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenčný hovor"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Popis"</string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b8e38ca..5cdbefb 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Aplikaciji omogoča branje sej namestitev. Tako lahko bere podrobnosti o aktivnih namestitvah paketov."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"zahtevanje paketov za namestitev"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Aplikaciji omogoča zahtevanje namestitve paketov."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tapnite dvakrat za nadzor povečave/pomanjšave"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Pripomočka ni bilo mogoče dodati."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Pojdi"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ponastavi"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – onemogočeno"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenčni klic"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Opis orodja"</string>
 </resources>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 761477e..ea33fff 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Lejon një aplikacion të lexojë sesionet e instalimit. Kjo e lejon atë të shohë detaje rreth instalimeve të paketave aktive."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"kërko paketat e instalimit"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Lejon që një aplikacion të kërkojë instalimin e paketave."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Trokit dy herë për të kontrolluar zmadhimin"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Nuk mundi të shtonte miniaplikacion."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Shko"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Rivendos tani"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> u çaktivizua"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonatë konferencë"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Këshilla për veglën"</string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f018116..19a1140 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1237,6 +1237,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозвољава апликацији да чита сесије инсталирања. То јој дозвољава да види детаље о активним инсталацијама пакета."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"захтевање пакета за инсталирање"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Омогућава да апликација захтева инсталацију пакета."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Додирните двапут за контролу зумирања"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Није могуће додати виџет."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Иди"</string>
@@ -1719,6 +1723,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Ресетуј"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Виџет <xliff:g id="LABEL">%1$s</xliff:g> је онемогућен"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференцијски позив"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Објашњење"</string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 93504cc..3a4fee3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Tillåt appen att läsa installationssessioner. Det ger den tillgång till uppgifter om aktiva paketinstallationer."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"begära installationspaket"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Tillåter att en app begär paketinstallation."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Peka två gånger för zoomkontroll"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Det gick inte att lägga till widgeten."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Kör"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Återställ nu"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> har inaktiverats"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenssamtal"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Beskrivning"</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8d69d01..4b75ff1 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1210,6 +1210,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Huruhusu programu kusoma vipindi vya kusanikisha. Hii huiruhusu kuona maelezo kuhusu usanikishaji wa programu unaoendelea."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"omba ruhusa ya kusakinisha vifurushi"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Huruhusu programu kuomba idhini ya kusakinisha vifurushi."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gonga mara mbili kwa udhibiti wa kuza"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Haikuweza kuongeza wijeti."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Nenda"</string>
@@ -1681,6 +1685,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Weka upya sasa"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> imezimwa"</string>
     <string name="conference_call" msgid="3751093130790472426">"Simu ya Kongamano"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Kidirisha cha vidokezo"</string>
 </resources>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 00fa2d9..6a54f82 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"நிறுவல் அமர்வுகளைப் படிக்க, பயன்பாட்டை அனுமதிக்கிறது. இது செயல்படும் தொகுப்பு நிறுவல்களைப் பற்றிய விவரங்களைப் பார்க்க அனுமதிக்கிறது."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"நிறுவல் தொகுப்புகளைக் கோருதல்"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"தொகுப்புகளின் நிறுவலைக் கோர, பயன்பாட்டை அனுமதிக்கும்."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"அளவை மாற்றுவதற்கான கட்டுப்பாட்டிற்கு, இருமுறை தட்டவும்"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"விட்ஜெட்டைச் சேர்க்க முடியவில்லை."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"செல்"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"இப்போதே மீட்டமை"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"முடக்கப்பட்டது: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"குழு அழைப்பு"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"உதவிக்குறிப்பு"</string>
 </resources>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 0e3a059..49b509c 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ఇన్‌స్టాల్ సెషన్‌లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది సక్రియ ప్యాకేజీ ఇన్‌స్టాలేషన్‌ల గురించి వివరాలను చూడటానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ఇన్‌స్టాల్ ప్యాకేజీలను అభ్యర్థించడం"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ప్యాకేజీల ఇన్‌స్టాలేషన్ అభ్యర్థించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"విడ్జెట్‌ను జోడించడం సాధ్యపడలేదు."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"వెళ్లు"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ఇప్పుడే రీసెట్ చేయి"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> నిలిపివేయబడింది"</string>
     <string name="conference_call" msgid="3751093130790472426">"కాన్ఫరెన్స్ కాల్"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"సాధనం చిట్కా"</string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 75afd4b..c4d72ab 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"อนุญาตให้แอปพลิเคชันอ่านเซสชันการติดตั้ง ซึ่งจะอนุญาตให้อ่านรายละเอียดเกี่ยวกับการติดตั้งแพ็กเกจที่ใช้งาน"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"ขอติดตั้งแพ็กเกจ"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"อนุญาตให้แอปพลิเคชันขอการติดตั้งแพ็กเกจ"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"ไป"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"รีเซ็ตทันที"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ปิดใช้ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"การประชุมสาย"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"เคล็ดลับเครื่องมือ"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 19d6b68..a4e1b26 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Pinapayagan ang isang application na magbasa ng mga session ng pag-install. Nagbibigay-daan ito upang makita ang mga detalye tungkol sa mga aktibong pag-install ng package."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"humiling ng mga package sa pag-install"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Pinapayagan ang isang application na hilingin ang pag-install ng mga package."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Tapikin ng dalawang beses para sa pagkontrol ng zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Hindi maidagdag ang widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Pumunta"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"I-reset ngayon"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Na-disable ang <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b626fb9..eacf16e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Bir uygulamanın yükleme oturumlarını okumasına izin verir. Bu, etkin paket yüklemeleriyle ilgili ayrıntıların görülmesine olanak tanır."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paket yükleme isteğinde bulunma"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Uygulamaya, paketleri yükleme isteğinde bulunma izni verir."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Zum denetimi için iki kez dokun"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget eklenemedi."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Git"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Şimdi sıfırla"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> devre dışı"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferans Çağrısı"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"İpucu"</string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 05341a7..4dbeb0d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1262,6 +1262,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Дозволяє додатку читати дані сеансів встановлення. Додаток може бачити деталі про активні встановлення пакетів."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"запитувати дані про пакети встановлення"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Додаток зможе надсилати запити на встановлення пакетів."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Двічі натис. для кер. масшт."</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Не вдалося додати віджет."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Йти"</string>
@@ -1755,6 +1759,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Скинути"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> вимкнено"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференц-виклик"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Спливаюча підказка"</string>
 </resources>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 40e6624..82253ac 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"ایک ایپلیکیشن کو انسٹال سیشنز پڑھنے کی اجازت دیتا ہے۔ یہ اسے فعال پیکیج انسٹالیشنز کے بارے میں تفصیلات دیکھنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"پیکجز انسٹال کرنے کی درخواست کریں"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"ایک ایپلیکیشن کو پیکجز انسٹال کرنے کی اجازت دیتی ہے۔"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"زوم کنٹرول کیلئے دوبار تھپتھپائیں"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"ویجٹس کو شامل نہیں کرسکا۔"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"جائیں"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"ابھی ری سیٹ کریں"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"غیر فعال کردہ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"کانفرنس کال"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"ٹول ٹپ"</string>
 </resources>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index a41b323..f44a6c9 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ilovaga o‘rnatilgan seanslarni o‘qish uchun ruxsat beradi. Bu unga faol paket o‘rnatmalari haqidagi ma’lumotlarni ko‘rish imkonini beradi."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"paketlarni o‘rnatish so‘rovini yuborish"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ilovaga paketlarni o‘rnatish so‘rovini yuborish imkonini beradi."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"O‘tish"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Asl holatga qaytarish"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> vidjeti o‘chirilgan"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferens-aloqa"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Maslahat oynasi"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index bed672d..21ad11c4 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Cho phép ứng dụng đọc phiên cài đặt. Thao tác này sẽ cho phép ứng dụng xem chi tiết về gói cài đặt đang hoạt động."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"yêu cầu gói cài đặt"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Cho phép ứng dụng yêu cầu cài đặt gói."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Nhấn hai lần để kiểm soát thu phóng"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Không thể thêm tiện ích."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Đến"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Đặt lại ngay bây giờ"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Đã tắt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Cuộc gọi nhiều bên"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Chú giải công cụ"</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 9051cae..68fec51 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -278,7 +278,7 @@
     <string name="permlab_statusBarService" msgid="4826835508226139688">"用作状态栏"</string>
     <string name="permdesc_statusBarService" msgid="716113660795976060">"允许以状态栏形式显示应用。"</string>
     <string name="permlab_expandStatusBar" msgid="1148198785937489264">"展开/收拢状态栏"</string>
-    <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允许应用展开或折叠状态栏。"</string>
+    <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"允许应用展开或收起状态栏。"</string>
     <string name="permlab_install_shortcut" msgid="4279070216371564234">"安装快捷方式"</string>
     <string name="permdesc_install_shortcut" msgid="8341295916286736996">"允许应用自行添加主屏幕快捷方式。"</string>
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"卸载快捷方式"</string>
@@ -740,7 +740,7 @@
     <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"添加小部件。"</string>
     <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"空白"</string>
     <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"已展开解锁区域。"</string>
-    <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"已折叠解锁区域。"</string>
+    <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"已收起解锁区域。"</string>
     <string name="keyguard_accessibility_widget" msgid="6527131039741808240">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g>小部件。"</string>
     <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"用户选择器"</string>
     <string name="keyguard_accessibility_status" msgid="8008264603935930611">"状态"</string>
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"允许应用读取安装会话。这样,应用将可以查看有关当前软件包安装的详情。"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"请求安装文件包"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"允许应用请求安装文件包。"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"双击可以进行缩放控制"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"无法添加小部件。"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"开始"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"立即重置"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"已停用的<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"电话会议"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"提示"</string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index cbb0901..d6b4cf7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"允許應用程式讀取安裝工作階段。應用程式將可查看目前安裝套裝的詳細資料。"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"要求安裝套件"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"允許應用程式要求安裝套件"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"輕觸兩下控制縮放"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"開始"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"立即重設"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"「<xliff:g id="LABEL">%1$s</xliff:g>」已停用"</string>
     <string name="conference_call" msgid="3751093130790472426">"會議通話"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"提示"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index d31dfd3..74b149d 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"允許應用程式讀取安裝工作階段。應用程式將可查看目前的套件安裝詳細資料。"</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"要求安裝套件"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"允許應用程式要求安裝套件。"</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"點兩下以進行縮放控制"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"無法新增小工具。"</string>
     <string name="ime_action_go" msgid="8320845651737369027">"開始"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"立即重設"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"已停用的<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"電話會議"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"工具提示"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9c99c76..d36b7fb 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1212,6 +1212,10 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ivumela uhlelo lokusebenza ukufunda izikhathi. Lokhu kuzolivumela ukubona imininingwane mayelana nokufaka kwephakethi esebenzayo."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"cela amaphakheji wokufaka"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ivumela uhlelo lokusebenza ukucela ukufakwa kwamaphakheji."</string>
+    <!-- no translation found for permlab_requestIgnoreBatteryOptimizations (8021256345643918264) -->
+    <skip />
+    <!-- no translation found for permdesc_requestIgnoreBatteryOptimizations (8359147856007447638) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Thepha kabili ukuthola ukulawula ukusondeza"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Yehlulekile ukwengeza i-widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Iya"</string>
@@ -1683,6 +1687,5 @@
     <string name="demo_user_inactivity_timeout_right_button" msgid="5019306703066964808">"Setha kabusha manje"</string>
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"I-<xliff:g id="LABEL">%1$s</xliff:g> ekhutshaziwe"</string>
     <string name="conference_call" msgid="3751093130790472426">"Ikholi yengqungquthela"</string>
-    <!-- no translation found for tooltip_popup_title (5253721848739260181) -->
-    <skip />
+    <string name="tooltip_popup_title" msgid="5253721848739260181">"Ithulithiphu"</string>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7045eaf..8c64484 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7508,6 +7508,8 @@
         <attr name="title" />
         <!-- @SystemApi Summary for the same preference as the title. @hide -->
         <attr name="summary" />
+        <!-- @SystemApi Whether trust agent can unlock a user profile @hide -->
+        <attr name="unlockProfile" format="boolean"/>
     </declare-styleable>
 
     <!-- =============================== -->
@@ -8378,13 +8380,13 @@
         <attr name="color" />
     </declare-styleable>
 
-    <!-- @hide Attributes which will be read by the Activity to intialize the 
+    <!-- @hide Attributes which will be read by the Activity to intialize the
                base activity TaskDescription. -->
     <declare-styleable name="ActivityTaskDescription">
         <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary 
                    color. -->
         <attr name="colorPrimary" />
-        <!-- @hide From Theme.colorBackground, used for the TaskDescription background 
+        <!-- @hide From Theme.colorBackground, used for the TaskDescription background
                    color. -->
         <attr name="colorBackground" />
     </declare-styleable>
diff --git a/core/tests/coretests/res/layout/messaging_linear_layout_test.xml b/core/tests/coretests/res/layout/messaging_linear_layout_test.xml
new file mode 100644
index 0000000..8ba3e07
--- /dev/null
+++ b/core/tests/coretests/res/layout/messaging_linear_layout_test.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<com.android.internal.widget.MessagingLinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:maxHeight="300px"
+    android:spacing="5px">
+
+</com.android.internal.widget.MessagingLinearLayout>
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
new file mode 100644
index 0000000..5dc07c2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.view.View.MeasureSpec;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class ImageFloatingTextViewTest {
+
+    private Context mContext;
+    private ImageFloatingTextView mView;
+    private TextView mTextView;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mView = new ImageFloatingTextView(mContext, null, 0, 0);
+        mTextView = new TextView(mContext, null, 0, 0);
+        mTextView.setMaxLines(9);
+    }
+
+    @Test
+    public void testEmpty() {
+        parametrizedTest("");
+    }
+
+    @Test
+    public void testSingleLine() {
+        parametrizedTest("Hello, World!");
+    }
+
+    @Test
+    public void testTwoLine() {
+        parametrizedTest("Hello, World!\nWhat a nice day!");
+    }
+
+    @Test
+    public void testShort() {
+        parametrizedTest("Hello, World! What a nice day! Let's try some more text. "
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet.");
+    }
+
+    @Test
+    public void testLong() {
+        parametrizedTest("Hello, World! What a nice day! Let's try some more text. "
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet."
+                + "Yada yada, yada yada. Lorem ipsum dolor sit amet.");
+    }
+
+    private void parametrizedTest(CharSequence text) {
+        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.AT_MOST);
+        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
+
+        mTextView.setText(text);
+        mView.setText(text);
+
+        mTextView.measure(widthMeasureSpec, heightMeasureSpec);
+        mView.measure(widthMeasureSpec, heightMeasureSpec);
+
+        assertEquals(mTextView.getMeasuredHeight(), mView.getMeasuredHeight());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
new file mode 100644
index 0000000..75b2c1d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Debug;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.core.deps.guava.base.Function;
+import android.support.test.filters.SmallTest;
+import android.view.LayoutInflater;
+import android.view.View.MeasureSpec;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+
+@SmallTest
+public class MessagingLinearLayoutTest {
+
+    public static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
+    public static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
+    private Context mContext;
+    private MessagingLinearLayout mView;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        // maxHeight: 300px
+        // spacing: 50px
+        mView = (MessagingLinearLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.messaging_linear_layout_test, null);
+    }
+
+    @Test
+    public void testSingleChild() {
+        FakeImageFloatingTextView child = fakeChild((i) -> 3);
+
+        mView.setNumIndentLines(2);
+        mView.addView(child);
+
+        mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        assertEquals(3, child.getNumIndentLines());
+        assertFalse(child.isHidden());
+        assertEquals(150, mView.getMeasuredHeight());
+    }
+
+    @Test
+    public void testLargeSmall() {
+        FakeImageFloatingTextView child1 = fakeChild((i) -> 3);
+        FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+        mView.setNumIndentLines(2);
+        mView.addView(child1);
+        mView.addView(child2);
+
+        mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        assertEquals(3, child1.getNumIndentLines());
+        assertEquals(0, child2.getNumIndentLines());
+        assertFalse(child1.isHidden());
+        assertFalse(child2.isHidden());
+        assertEquals(205, mView.getMeasuredHeight());
+    }
+
+    @Test
+    public void testSmallSmall() {
+        FakeImageFloatingTextView child1 = fakeChild((i) -> 1);
+        FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+        mView.setNumIndentLines(2);
+        mView.addView(child1);
+        mView.addView(child2);
+
+        mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        assertEquals(2, child1.getNumIndentLines());
+        assertEquals(1, child2.getNumIndentLines());
+        assertFalse(child1.isHidden());
+        assertFalse(child2.isHidden());
+        assertEquals(105, mView.getMeasuredHeight());
+    }
+
+    @Test
+    public void testLargeLarge() {
+        FakeImageFloatingTextView child1 = fakeChild((i) -> 7);
+        FakeImageFloatingTextView child2 = fakeChild((i) -> 7);
+
+        mView.setNumIndentLines(2);
+        mView.addView(child1);
+        mView.addView(child2);
+
+        mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        assertEquals(3, child2.getNumIndentLines());
+        assertTrue(child1.isHidden());
+        assertFalse(child2.isHidden());
+        assertEquals(300, mView.getMeasuredHeight());
+    }
+
+    @Test
+    public void testLargeSmall_largeWrapsWith3indentbutnot3_andHitsMax() {
+        FakeImageFloatingTextView child1 = fakeChild((i) -> i > 2 ? 5 : 4);
+        FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+        mView.setNumIndentLines(2);
+        mView.addView(child1);
+        mView.addView(child2);
+
+        mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        assertTrue(child1.isHidden());
+        assertFalse(child2.isHidden());
+        assertEquals(50, mView.getMeasuredHeight());
+        assertEquals(2, child2.getNumIndentLines());
+    }
+
+    @Test
+    public void testLargeSmall_largeWrapsWith3indentbutnot3() {
+        FakeImageFloatingTextView child1 = fakeChild((i) -> i > 2 ? 4 : 3);
+        FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
+
+        mView.setNumIndentLines(2);
+        mView.addView(child1);
+        mView.addView(child2);
+
+        mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        assertFalse(child1.isHidden());
+        assertFalse(child2.isHidden());
+        assertEquals(255, mView.getMeasuredHeight());
+        assertEquals(3, child1.getNumIndentLines());
+        assertEquals(0, child2.getNumIndentLines());
+    }
+
+    private class FakeImageFloatingTextView extends ImageFloatingTextView {
+
+        public static final int LINE_HEIGHT = 50;
+        private final Function<Integer, Integer> mLinesForIndent;
+        private int mNumIndentLines;
+
+        public FakeImageFloatingTextView(Context context,
+                Function<Integer, Integer> linesForIndent) {
+            super(context, null, 0, 0);
+            mLinesForIndent = linesForIndent;
+        }
+
+        @Override
+        public boolean setNumIndentLines(int lines) {
+            boolean changed = (mNumIndentLines != lines);
+            mNumIndentLines = lines;
+            return changed;
+        }
+
+        public int getNumIndentLines() {
+            return mNumIndentLines;
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            setMeasuredDimension(
+                    getDefaultSize(500, widthMeasureSpec),
+                    resolveSize(getDesiredHeight(), heightMeasureSpec));
+        }
+
+        @Override
+        public int getLineCount() {
+            return mLinesForIndent.apply(mNumIndentLines);
+        }
+
+        public int getDesiredHeight() {
+            return LINE_HEIGHT * getLineCount();
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            // swallow
+        }
+
+        public boolean isHidden() {
+            MessagingLinearLayout.LayoutParams lp =
+                    (MessagingLinearLayout.LayoutParams) getLayoutParams();
+            try {
+                Field hide = MessagingLinearLayout.LayoutParams.class.getDeclaredField("hide");
+                hide.setAccessible(true);
+                return hide.getBoolean(lp);
+            } catch (ReflectiveOperationException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private FakeImageFloatingTextView fakeChild(Function<Integer,Integer> linesForIndent) {
+        return new FakeImageFloatingTextView(mContext, linesForIndent);
+    }
+}
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 6e415f4..46a0d9b 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -12,6 +12,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += src/android/util/IRemoteMemoryIntArray.aidl
 
+LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
+
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     frameworks-base-testutils \
diff --git a/core/tests/utiltests/jni/Android.bp b/core/tests/utiltests/jni/Android.bp
new file mode 100644
index 0000000..e9a4144
--- /dev/null
+++ b/core/tests/utiltests/jni/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 The Android Open Source Project

+//

+// Licensed under the Apache License, Version 2.0 (the "License");

+// you may not use this file except in compliance with the License.

+// You may obtain a copy of the License at

+//

+//      http://www.apache.org/licenses/LICENSE-2.0

+//

+// Unless required by applicable law or agreed to in writing, software

+// distributed under the License is distributed on an "AS IS" BASIS,

+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+// See the License for the specific language governing permissions and

+// limitations under the License.

+

+cc_library_shared {

+    name: "libmemoryintarraytest",

+    shared_libs: [

+        "libcutils",

+    ],

+    clang: true,

+    stl: "libc++",

+    srcs: [

+        "registration.cpp",

+        "android_util_MemoryIntArrayTest.cpp",

+    ],

+    cflags: ["-Werror"],

+}
\ No newline at end of file
diff --git a/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp b/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp
new file mode 100644
index 0000000..57ee2d5
--- /dev/null
+++ b/core/tests/utiltests/jni/android_util_MemoryIntArrayTest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <jni.h>
+#include <cutils/ashmem.h>
+#include <linux/ashmem.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+jint android_util_MemoryIntArrayTest_createAshmem(__attribute__((unused)) JNIEnv* env,
+        __attribute__((unused)) jobject clazz,
+        jstring name, jint size)
+{
+
+    if (name == NULL) {
+        return -1;
+    }
+
+    if (size < 0) {
+        return -1;
+    }
+
+    const char* nameStr = env->GetStringUTFChars(name, NULL);
+    const int ashmemSize = sizeof(std::atomic_int) * size;
+    int fd = ashmem_create_region(nameStr, ashmemSize);
+    env->ReleaseStringUTFChars(name, nameStr);
+
+    if (fd < 0) {
+        return -1;
+    }
+
+    int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
+    if (setProtResult < 0) {
+        return -1;
+    }
+
+    return fd;
+}
+
+void android_util_MemoryIntArrayTest_setAshmemSize(__attribute__((unused)) JNIEnv* env,
+        __attribute__((unused)) jobject clazz, jint fd, jint size)
+{
+    if (fd < 0) {
+        return;
+    }
+
+    if (size < 0) {
+        return;
+    }
+
+    ioctl(fd, ASHMEM_SET_SIZE, size);
+}
diff --git a/core/tests/utiltests/jni/registration.cpp b/core/tests/utiltests/jni/registration.cpp
new file mode 100644
index 0000000..0c84d98
--- /dev/null
+++ b/core/tests/utiltests/jni/registration.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+extern jint android_util_MemoryIntArrayTest_createAshmem(JNIEnv* env,
+        jobject clazz, jstring name, jint size);
+extern void android_util_MemoryIntArrayTest_setAshmemSize(JNIEnv* env,
+       jobject clazz, jint fd, jint size);
+
+extern "C" {
+    JNIEXPORT jint JNICALL Java_android_util_MemoryIntArrayTest_nativeCreateAshmem(
+            JNIEnv * env, jobject obj, jstring name, jint size);
+    JNIEXPORT void JNICALL Java_android_util_MemoryIntArrayTest_nativeSetAshmemSize(
+            JNIEnv * env, jobject obj, jint fd, jint size);
+};
+
+JNIEXPORT jint JNICALL Java_android_util_MemoryIntArrayTest_nativeCreateAshmem(
+        __attribute__((unused)) JNIEnv * env,__attribute__((unused)) jobject obj,
+        jstring name, jint size)
+{
+    return android_util_MemoryIntArrayTest_createAshmem(env, obj, name, size);
+}
+
+JNIEXPORT void JNICALL Java_android_util_MemoryIntArrayTest_nativeSetAshmemSize(
+        __attribute__((unused)) JNIEnv * env,__attribute__((unused)) jobject obj,
+        jint fd, jint size)
+{
+    android_util_MemoryIntArrayTest_setAshmemSize(env, obj, fd, size);
+}
diff --git a/core/tests/utiltests/src/android/util/IRemoteMemoryIntArray.aidl b/core/tests/utiltests/src/android/util/IRemoteMemoryIntArray.aidl
index 0a65fff2..10d14f1 100644
--- a/core/tests/utiltests/src/android/util/IRemoteMemoryIntArray.aidl
+++ b/core/tests/utiltests/src/android/util/IRemoteMemoryIntArray.aidl
@@ -20,11 +20,12 @@
 
 interface IRemoteMemoryIntArray {
     MemoryIntArray peekInstance();
-    void create(int size, boolean clientWritable);
+    void create(int size);
     boolean isWritable();
     int get(int index);
     void set(int index, int value);
     int size();
     void close();
     boolean isClosed();
+    void accessLastElementInRemoteProcess(in MemoryIntArray array);
 }
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 129e6b7..85817bb 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -28,14 +28,22 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.reflect.Field;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 public class MemoryIntArrayTest {
+    static {
+        System.loadLibrary("cutils");
+        System.loadLibrary("memoryintarraytest");
+    }
 
     @Test
     public void testSize() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(3, false);
+            array = new MemoryIntArray(3);
             assertEquals("size must be three", 3, array.size());
         } finally {
             IoUtils.closeQuietly(array);
@@ -46,7 +54,7 @@
     public void testGetSet() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(3, false);
+            array = new MemoryIntArray(3);
 
             array.set(0, 1);
             array.set(1, 2);
@@ -64,7 +72,7 @@
     public void testWritable() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(3, true);
+            array = new MemoryIntArray(3);
             assertTrue("Must be mutable", array.isWritable());
         } finally {
             IoUtils.closeQuietly(array);
@@ -75,7 +83,7 @@
     public void testClose() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(3, false);
+            array = new MemoryIntArray(3);
             array.close();
             assertTrue("Must be closed", array.isClosed());
         } finally {
@@ -90,7 +98,7 @@
         MemoryIntArray firstArray = null;
         MemoryIntArray secondArray = null;
         try {
-            firstArray = new MemoryIntArray(3, false);
+            firstArray = new MemoryIntArray(3);
 
             firstArray.set(0, 1);
             firstArray.set(1, 2);
@@ -117,7 +125,7 @@
     public void testInteractOnceClosed() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(3, false);
+            array = new MemoryIntArray(3);
             array.close();
 
             array.close();
@@ -160,7 +168,7 @@
     public void testInteractPutOfBounds() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(3, false);
+            array = new MemoryIntArray(3);
 
             try {
                 array.get(-1);
@@ -198,7 +206,7 @@
     public void testOverMaxSize() throws Exception {
         MemoryIntArray array = null;
         try {
-            array = new MemoryIntArray(MemoryIntArray.getMaxSize() + 1, false);
+            array = new MemoryIntArray(MemoryIntArray.getMaxSize() + 1);
             fail("Cannot use over max size");
         } catch (IllegalArgumentException e) {
             /* expected */
@@ -209,7 +217,7 @@
 
     @Test
     public void testNotMutableByUnprivilegedClients() throws Exception {
-        RemoteIntArray remoteIntArray = new RemoteIntArray(1, false);
+        RemoteIntArray remoteIntArray = new RemoteIntArray(1);
         try {
             assertNotNull("Couldn't get remote instance", remoteIntArray);
             MemoryIntArray localIntArray = remoteIntArray.peekInstance();
@@ -230,4 +238,64 @@
             remoteIntArray.destroy();
         }
     }
+
+    @Test
+    public void testAshmemSizeMatchesMemoryIntArraySize() throws Exception {
+        boolean success = false;
+
+        // Get a handle to a remote process to send the fd
+        RemoteIntArray remoteIntArray = new RemoteIntArray(1);
+        try {
+            // Let us try 100 times
+            for (int i = 0; i < 100; i++) {
+                // Create a MemoryIntArray to muck with
+                MemoryIntArray array = new MemoryIntArray(1);
+
+                // Create the fd to stuff in the MemoryIntArray
+                final int fd = nativeCreateAshmem("foo", 1);
+
+                // Replace the fd with our ahsmem region
+                Field fdFiled = MemoryIntArray.class.getDeclaredField("mFd");
+                fdFiled.setAccessible(true);
+                fdFiled.set(array, fd);
+
+                CountDownLatch countDownLatch = new CountDownLatch(2);
+
+                new Thread() {
+                    @Override
+                    public void run() {
+                        for (int i = 2; i < Integer.MAX_VALUE; i++) {
+                            if (countDownLatch.getCount() == 1) {
+                                countDownLatch.countDown();
+                                return;
+                            }
+                            nativeSetAshmemSize(fd, i);
+                        }
+                    }
+                }.start();
+
+                try {
+                    remoteIntArray.accessLastElementInRemoteProcess(array);
+                } catch (IllegalArgumentException e) {
+                    success = true;
+                }
+
+                countDownLatch.countDown();
+                countDownLatch.await(1000, TimeUnit.MILLISECONDS);
+
+                if (success) {
+                    break;
+                }
+            }
+        } finally {
+            remoteIntArray.destroy();
+        }
+
+        if (!success) {
+            fail("MemoryIntArray should catch ahshmem size changing under it");
+        }
+    }
+
+    private native int nativeCreateAshmem(String name, int size);
+    private native void nativeSetAshmemSize(int fd, int size);
 }
diff --git a/core/tests/utiltests/src/android/util/RemoteIntArray.java b/core/tests/utiltests/src/android/util/RemoteIntArray.java
index 10c325f..7dc3400 100644
--- a/core/tests/utiltests/src/android/util/RemoteIntArray.java
+++ b/core/tests/utiltests/src/android/util/RemoteIntArray.java
@@ -40,7 +40,7 @@
 
     private android.util.IRemoteMemoryIntArray mRemoteInstance;
 
-    public RemoteIntArray(int size, boolean clientWritable) throws IOException, TimeoutException {
+    public RemoteIntArray(int size) throws IOException, TimeoutException {
         mIntent.setComponent(new ComponentName(InstrumentationRegistry.getContext(),
                 RemoteMemoryIntArrayService.class));
         synchronized (mLock) {
@@ -48,7 +48,7 @@
                 bindLocked();
             }
             try {
-                mRemoteInstance.create(size, clientWritable);
+                mRemoteInstance.create(size);
             } catch (RemoteException e) {
                 throw new IOException(e);
             }
@@ -148,6 +148,14 @@
         }
     }
 
+    public void accessLastElementInRemoteProcess(MemoryIntArray array) {
+        try {
+            mRemoteInstance.accessLastElementInRemoteProcess(array);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         synchronized (mLock) {
diff --git a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
index 35ae9a7..9264c6c 100644
--- a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
+++ b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
@@ -35,10 +35,10 @@
         return new android.util.IRemoteMemoryIntArray.Stub() {
 
             @Override
-            public void create(int size, boolean clientWritable) {
+            public void create(int size) {
                 synchronized (mLock) {
                     try {
-                        mArray = new MemoryIntArray(size, clientWritable);
+                        mArray = new MemoryIntArray(size);
                     } catch (IOException e) {
                         throw new IllegalStateException(e);
                     }
@@ -109,6 +109,15 @@
                     return mArray.isClosed();
                 }
             }
+
+            @Override
+            public void accessLastElementInRemoteProcess(MemoryIntArray array) {
+                try {
+                    array.get(array.size() - 1);
+                } catch (IOException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
         };
     }
 }
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 0011002..7bab189 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -21,10 +21,11 @@
 # This contains the junit.framework classes that were in Android API level 25.
 include $(CLEAR_VARS)
 
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := core-junit-static
-
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_MODULE := legacy-test
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+LOCAL_STATIC_JAVA_LIBRARIES := core-junit-static
 
 include $(BUILD_JAVA_LIBRARY)
 
@@ -34,7 +35,7 @@
 # This contains the android.test.PerformanceTestCase class only
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := ../core/java/android/test/PerformanceTestCase.java
+LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
 LOCAL_MODULE := legacy-performance-test-hostdex
 
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
diff --git a/core/java/android/test/AndroidTestCase.java b/legacy-test/src/android/test/AndroidTestCase.java
similarity index 100%
rename from core/java/android/test/AndroidTestCase.java
rename to legacy-test/src/android/test/AndroidTestCase.java
diff --git a/core/java/android/test/FlakyTest.java b/legacy-test/src/android/test/FlakyTest.java
similarity index 100%
rename from core/java/android/test/FlakyTest.java
rename to legacy-test/src/android/test/FlakyTest.java
diff --git a/core/java/android/test/InstrumentationTestCase.java b/legacy-test/src/android/test/InstrumentationTestCase.java
similarity index 100%
rename from core/java/android/test/InstrumentationTestCase.java
rename to legacy-test/src/android/test/InstrumentationTestCase.java
diff --git a/core/java/android/test/InstrumentationTestSuite.java b/legacy-test/src/android/test/InstrumentationTestSuite.java
similarity index 100%
rename from core/java/android/test/InstrumentationTestSuite.java
rename to legacy-test/src/android/test/InstrumentationTestSuite.java
diff --git a/core/java/android/test/PerformanceTestCase.java b/legacy-test/src/android/test/PerformanceTestCase.java
similarity index 100%
rename from core/java/android/test/PerformanceTestCase.java
rename to legacy-test/src/android/test/PerformanceTestCase.java
diff --git a/core/java/android/test/RepetitiveTest.java b/legacy-test/src/android/test/RepetitiveTest.java
similarity index 100%
rename from core/java/android/test/RepetitiveTest.java
rename to legacy-test/src/android/test/RepetitiveTest.java
diff --git a/core/java/android/test/UiThreadTest.java b/legacy-test/src/android/test/UiThreadTest.java
similarity index 100%
rename from core/java/android/test/UiThreadTest.java
rename to legacy-test/src/android/test/UiThreadTest.java
diff --git a/core/java/android/test/package.html b/legacy-test/src/android/test/package.html
similarity index 100%
rename from core/java/android/test/package.html
rename to legacy-test/src/android/test/package.html
diff --git a/core/java/android/test/suitebuilder/annotation/LargeTest.java b/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java
similarity index 100%
rename from core/java/android/test/suitebuilder/annotation/LargeTest.java
rename to legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java
diff --git a/core/java/android/test/suitebuilder/annotation/MediumTest.java b/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java
similarity index 100%
rename from core/java/android/test/suitebuilder/annotation/MediumTest.java
rename to legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java
diff --git a/core/java/android/test/suitebuilder/annotation/SmallTest.java b/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java
similarity index 100%
rename from core/java/android/test/suitebuilder/annotation/SmallTest.java
rename to legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java
diff --git a/core/java/android/test/suitebuilder/annotation/Smoke.java b/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java
similarity index 100%
rename from core/java/android/test/suitebuilder/annotation/Smoke.java
rename to legacy-test/src/android/test/suitebuilder/annotation/Smoke.java
diff --git a/core/java/android/test/suitebuilder/annotation/Suppress.java b/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java
similarity index 100%
rename from core/java/android/test/suitebuilder/annotation/Suppress.java
rename to legacy-test/src/android/test/suitebuilder/annotation/Suppress.java
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cf571e9..51b1fac 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -46,6 +46,7 @@
     renderthread/RenderTask.cpp \
     renderthread/RenderThread.cpp \
     renderthread/TimeLord.cpp \
+    renderthread/Frame.cpp \
     thread/TaskManager.cpp \
     utils/Blur.cpp \
     utils/GLUtils.cpp \
@@ -182,6 +183,7 @@
     external/skia/src/core \
     external/skia/src/effects \
     external/skia/src/image \
+    external/skia/src/utils \
     external/harfbuzz_ng/src \
     external/freetype/include
 
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index 7e2c28c..489039c 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -202,17 +202,17 @@
 // Clip
 ///////////////////////////////////////////////////////////////////////////////
 
-bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
     mSnapshot->clip(Rect(left, top, right, bottom), op);
     return !mSnapshot->clipIsEmpty();
 }
 
-bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) {
+bool CanvasState::clipPath(const SkPath* path, SkClipOp op) {
     mSnapshot->clipPath(*path, op);
     return !mSnapshot->clipIsEmpty();
 }
 
-bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
+bool CanvasState::clipRegion(const SkRegion* region, SkClipOp op) {
     mSnapshot->clipRegionTransformed(*region, op);
     return !mSnapshot->clipIsEmpty();
 }
@@ -225,7 +225,7 @@
     bool outlineIsRounded = MathUtils::isPositive(radius);
     if (!outlineIsRounded || currentTransform()->isSimple()) {
         // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
-        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op);
+        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, kIntersect_SkClipOp);
     }
     if (outlineIsRounded) {
         setClippingRoundRect(allocator, bounds, radius, false);
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 22c7e8a..a805597 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -18,6 +18,7 @@
 
 #include "Snapshot.h"
 
+#include <SkClipOp.h>
 #include <SkMatrix.h>
 #include <SkPath.h>
 #include <SkRegion.h>
@@ -121,9 +122,9 @@
 
     bool quickRejectConservative(float left, float top, float right, float bottom) const;
 
-    bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
-    bool clipPath(const SkPath* path, SkRegion::Op op);
-    bool clipRegion(const SkRegion* region, SkRegion::Op op);
+    bool clipRect(float left, float top, float right, float bottom, SkClipOp op);
+    bool clipPath(const SkPath* path, SkClipOp op);
+    bool clipRegion(const SkRegion* region, SkClipOp op);
 
     /**
      * Sets a "clipping outline", which is independent from the regular clip.
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 245db1d..5b683e0 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -120,7 +120,7 @@
     mCanvasState.save(SaveFlags::MatrixClip);
     mCanvasState.translate(tx, ty);
     mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-            SkRegion::kIntersect_Op);
+            kIntersect_SkClipOp);
     deferNodePropsAndOps(renderNode);
     mCanvasState.restore();
 }
@@ -262,7 +262,7 @@
         Rect clipRect;
         properties.getClippingRectForFlags(clipFlags, &clipRect);
         mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-                SkRegion::kIntersect_Op);
+                kIntersect_SkClipOp);
     }
 
     if (properties.getRevealClip().willClip()) {
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 408159b..74a8395 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -82,8 +82,15 @@
         return CopyResult::UnknownError;
     }
 
-    CopyResult copyResult = copyImageInto(sourceImage, texTransform, graphicBuffer->getWidth(),
-            graphicBuffer->getHeight(), srcRect, bitmap);
+    uint32_t width = graphicBuffer->getWidth();
+    uint32_t height = graphicBuffer->getHeight();
+    // If this is a 90 or 270 degree rotation we need to swap width/height
+    // This is a fuzzy way of checking that.
+    if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
+        std::swap(width, height);
+    }
+    CopyResult copyResult = copyImageInto(sourceImage, texTransform, width, height,
+            srcRect, bitmap);
 
     // All we're flushing & finishing is the deletion of the texture since
     // copyImageInto already did a major flush & finish as an implicit
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 9d18484..96c6d29 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -234,13 +234,13 @@
     SkRect bounds = path.getBounds();
     return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
 }
-bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
     return mState.clipRect(left, top, right, bottom, op);
 }
-bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) {
     return mState.clipPath(path, op);
 }
-bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+bool RecordingCanvas::clipRegion(const SkRegion* region, SkClipOp op) {
     return mState.clipRegion(region, op);
 }
 
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index f93e8b8..5d49385 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -131,9 +131,10 @@
     virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
     virtual bool quickRejectPath(const SkPath& path) const override;
 
-    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) override;
-    virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
-    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+    virtual bool clipRect(float left, float top, float right, float bottom,
+            SkClipOp op) override;
+    virtual bool clipPath(const SkPath* path, SkClipOp op) override;
+    virtual bool clipRegion(const SkRegion* region, SkClipOp op) override;
 
     // Misc
     virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6786a69..8ab57c9d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -78,13 +78,13 @@
 public:
     explicit ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
 
-    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+    virtual void clipRect(const SkRect& rect, SkClipOp op, bool antialias) {
         m_dstCanvas->clipRect(rect, op, antialias);
     }
-    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+    virtual void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) {
         m_dstCanvas->clipRRect(rrect, op, antialias);
     }
-    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+    virtual void clipPath(const SkPath& path, SkClipOp op, bool antialias) {
         m_dstCanvas->clipPath(path, op, antialias);
     }
 
@@ -218,11 +218,11 @@
 
 class SkiaCanvas::Clip {
 public:
-    Clip(const SkRect& rect, SkRegion::Op op, const SkMatrix& m)
+    Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
         : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {}
-    Clip(const SkRRect& rrect, SkRegion::Op op, const SkMatrix& m)
+    Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
         : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
-    Clip(const SkPath& path, SkRegion::Op op, const SkMatrix& m)
+    Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
         : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
 
     void apply(SkCanvas* canvas) const {
@@ -247,9 +247,9 @@
         Path,
     };
 
-    Type            mType;
-    SkRegion::Op    mOp;
-    SkMatrix        mMatrix;
+    Type        mType;
+    SkClipOp    mOp;
+    SkMatrix    mMatrix;
 
     // These are logically a union (tracked separately due to non-POD path).
     SkTLazy<SkPath> mPath;
@@ -293,7 +293,7 @@
 }
 
 template <typename T>
-void SkiaCanvas::recordClip(const T& clip, SkRegion::Op op) {
+void SkiaCanvas::recordClip(const T& clip, SkClipOp op) {
     // Only need tracking when in a partial save frame which
     // doesn't restore the clip.
     const SaveRec* rec = this->currentSaveRec();
@@ -397,14 +397,14 @@
     return mCanvas->quickReject(path);
 }
 
-bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
     SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
     this->recordClip(rect, op);
     mCanvas->clipRect(rect, op);
     return !mCanvas->isClipEmpty();
 }
 
-bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
     SkRRect roundRect;
     if (path->isRRect(&roundRect)) {
         this->recordClip(roundRect, op);
@@ -416,7 +416,7 @@
     return !mCanvas->isClipEmpty();
 }
 
-bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+bool SkiaCanvas::clipRegion(const SkRegion* region, SkClipOp op) {
     SkPath rgnPath;
     if (region->getBoundaryPath(&rgnPath)) {
         // The region is specified in device space.
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 4f1d857..9639ebd 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -92,9 +92,9 @@
     virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
     virtual bool quickRejectPath(const SkPath& path) const override;
     virtual bool clipRect(float left, float top, float right, float bottom,
-            SkRegion::Op op) override;
-    virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
-    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+            SkClipOp op) override;
+    virtual bool clipPath(const SkPath* path, SkClipOp op) override;
+    virtual bool clipRegion(const SkRegion* region, SkClipOp op) override;
 
     virtual SkDrawFilter* getDrawFilter() override;
     virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
@@ -174,7 +174,7 @@
     void recordPartialSave(SaveFlags::Flags flags);
 
     template<typename T>
-    void recordClip(const T&, SkRegion::Op);
+    void recordClip(const T&, SkClipOp);
     void applyPersistentClips(size_t clipStartIndex);
 
     void drawPoints(const float* points, int count, const SkPaint& paint,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 5978abc..75396f7 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -430,21 +430,21 @@
     }
 }
 
-void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
     mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
 }
 
-void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
     SkPath path;
     path.addRRect(roundRect);
     mCanvas->clipPath(&path, op);
 }
 
-void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
     mCanvas->clipPath(&path, op);
 }
 
-void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkClipOp op) {
     mCanvas->clipRegion(&region, op);
 }
 
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index 0111815..badcc1d 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -90,10 +90,10 @@
                              const SkPoint texCoords[4], SkBlendMode,
                              const SkPaint& paint) override;
 
-    virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
-    virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
-    virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
-    virtual void onClipRegion(const SkRegion&, SkRegion::Op) override;
+    virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
+    virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
+    virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
+    virtual void onClipRegion(const SkRegion&, SkClipOp) override;
 
 private:
     Canvas* mCanvas;
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index f61c3e9..3f08009 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -72,19 +72,19 @@
 // Clipping
 ///////////////////////////////////////////////////////////////////////////////
 
-void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
+void Snapshot::clipRegionTransformed(const SkRegion& region, SkClipOp op) {
     flags |= Snapshot::kFlagClipSet;
-    mClipArea->clipRegion(region, op);
+    mClipArea->clipRegion(region, static_cast<SkRegion::Op>(op));
 }
 
-void Snapshot::clip(const Rect& localClip, SkRegion::Op op) {
+void Snapshot::clip(const Rect& localClip, SkClipOp op) {
     flags |= Snapshot::kFlagClipSet;
-    mClipArea->clipRectWithTransform(localClip, transform, op);
+    mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op));
 }
 
-void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
+void Snapshot::clipPath(const SkPath& path, SkClipOp op) {
     flags |= Snapshot::kFlagClipSet;
-    mClipArea->clipPathWithTransform(path, transform, op);
+    mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op));
 }
 
 void Snapshot::setClip(float left, float top, float right, float bottom) {
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 4ab5830..287e58a 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -23,6 +23,7 @@
 #include <utils/RefBase.h>
 #include <ui/Region.h>
 
+#include <SkClipOp.h>
 #include <SkRegion.h>
 
 #include "ClipArea.h"
@@ -107,25 +108,25 @@
      * the specified operation. The specified rectangle is transformed
      * by this snapshot's trasnformation.
      */
-    void clip(const Rect& localClip, SkRegion::Op op);
+    void clip(const Rect& localClip, SkClipOp op);
 
     /**
      * Modifies the current clip with the new clip rectangle and
      * the specified operation. The specified rectangle is considered
      * already transformed.
      */
-    void clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
+    void clipTransformed(const Rect& r, SkClipOp op = kIntersect_SkClipOp);
 
     /**
      * Modifies the current clip with the specified region and operation.
      * The specified region is considered already transformed.
      */
-    void clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
+    void clipRegionTransformed(const SkRegion& region, SkClipOp op);
 
     /**
      * Modifies the current clip with the specified path and operation.
      */
-    void clipPath(const SkPath& path, SkRegion::Op op);
+    void clipPath(const SkPath& path, SkClipOp op);
 
     /**
      * Sets the current clip.
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index f4ffa7a..208107f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -308,7 +308,7 @@
 
 void ClipPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath,
         float strokeScale, const SkMatrix& matrix, bool useStagingData){
-    outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
+    outCanvas->clipPath(renderPath);
 }
 
 Group::Group(const Group& group) : Node(group) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index d3e765d..64ef866 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -16,6 +16,7 @@
 #include "Bitmap.h"
 
 #include "Caches.h"
+#include "renderthread/EglManager.h"
 #include "renderthread/RenderThread.h"
 #include "renderthread/RenderProxy.h"
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d7839b4..e7b6b2d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -182,9 +182,9 @@
     virtual bool quickRejectPath(const SkPath& path) const = 0;
 
     virtual bool clipRect(float left, float top, float right, float bottom,
-            SkRegion::Op op = SkRegion::kIntersect_Op) = 0;
-    virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
-    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+            SkClipOp op) = 0;
+    virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
+    virtual bool clipRegion(const SkRegion* region, SkClipOp op) = 0;
 
     // filters
     virtual SkDrawFilter* getDrawFilter() = 0;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4b34c7c..14cc449 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -68,7 +68,7 @@
         if (pendingClip && !pendingClip->contains(rect)) {
             canvas->clipRect(*pendingClip);
         }
-        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkRegion::kIntersect_Op, true);
+        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), kIntersect_SkClipOp, true);
     } else {
         if (pendingClip) {
             (void)rect.intersect(*pendingClip);
@@ -263,7 +263,7 @@
     }
 
     if (properties.getRevealClip().willClip()) {
-        canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true);
+        canvas->clipPath(*properties.getRevealClip().getPath(), kIntersect_SkClipOp, true);
     } else if (properties.getOutline().willClip()) {
         clipOutline(properties.getOutline(), canvas, pendingClip);
         pendingClip = nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 7f3474a..c8258f7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -19,6 +19,7 @@
 #include "DeferredLayerUpdater.h"
 #include "LayerDrawable.h"
 #include "renderthread/EglManager.h"
+#include "renderthread/Frame.h"
 #include "renderstate/RenderState.h"
 #include "SkiaPipeline.h"
 #include "SkiaProfileRenderer.h"
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index c9432a1..d77aa48 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -99,7 +99,7 @@
             int saveCount = layerCanvas->save();
             SkASSERT(saveCount == 1);
 
-            layerCanvas->clipRect(layerDamage.toSkRect(), SkRegion::kReplace_Op);
+            layerCanvas->clipRect(layerDamage.toSkRect(), kReplace_SkClipOp);
 
             auto savedLightCenter = mLightCenter;
             // map current light center into RenderNode's coordinate space
@@ -225,7 +225,7 @@
         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
         SkCanvas* canvas) {
 
-    canvas->clipRect(clip, SkRegion::kReplace_Op);
+    canvas->clipRect(clip, kReplace_SkClipOp);
 
     if (!opaque) {
         canvas->clear(SK_ColorTRANSPARENT);
@@ -275,7 +275,7 @@
             const float dy = backdropBounds.top - contentDrawBounds.top;
             canvas->translate(dx, dy);
             // It gets cropped against the bounds of the backdrop to stay inside.
-            canvas->clipRect(clip, SkRegion::kIntersect_Op);
+            canvas->clipRect(clip);
         }
 
         RenderNodeDrawable root(node.get(), canvas);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index ca394b2..0d3f4ef 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -17,7 +17,7 @@
 #include "SkiaVulkanPipeline.h"
 
 #include "DeferredLayerUpdater.h"
-#include "renderthread/EglManager.h" // needed for Frame
+#include "renderthread/Frame.h"
 #include "Readback.h"
 #include "renderstate/RenderState.h"
 #include "SkiaPipeline.h"
@@ -58,8 +58,7 @@
         return Frame(-1, -1, 0);
     }
 
-    // TODO: support buffer age if Vulkan API can do it
-    Frame frame(backBuffer->width(), backBuffer->height(), 0);
+    Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface));
     return frame;
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c561c86..1b3bf96 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -20,6 +20,7 @@
 #include "AnimationContext.h"
 #include "Caches.h"
 #include "EglManager.h"
+#include "Frame.h"
 #include "LayerUpdateQueue.h"
 #include "Properties.h"
 #include "RenderThread.h"
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index de95bee..02021fc 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -19,6 +19,7 @@
 #include "Texture.h"
 #include "Caches.h"
 #include "DeviceInfo.h"
+#include "Frame.h"
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
@@ -74,24 +75,6 @@
     bool setDamage = false;
 } EglExtensions;
 
-void Frame::map(const SkRect& in, EGLint* out) const {
-    /* The rectangles are specified relative to the bottom-left of the surface
-     * and the x and y components of each rectangle specify the bottom-left
-     * position of that rectangle.
-     *
-     * HWUI does everything with 0,0 being top-left, so need to map
-     * the rect
-     */
-    SkIRect idirty;
-    in.roundOut(&idirty);
-    EGLint y = mHeight - (idirty.y() + idirty.height());
-    // layout: {x, y, width, height}
-    out[0] = idirty.x();
-    out[1] = y;
-    out[2] = idirty.width();
-    out[3] = idirty.height();
-}
-
 EglManager::EglManager(RenderThread& thread)
         : mRenderThread(thread)
         , mEglDisplay(EGL_NO_DISPLAY)
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index b12522e..0251925 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -26,36 +26,8 @@
 namespace uirenderer {
 namespace renderthread {
 
+class Frame;
 class RenderThread;
-class EglManager;
-
-class Frame {
-public:
-    Frame(EGLint width, EGLint height, EGLint bufferAge)
-            : mWidth(width)
-            , mHeight(height)
-            , mBufferAge(bufferAge) { }
-
-    EGLint width() const { return mWidth; }
-    EGLint height() const { return mHeight; }
-
-    // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
-    // for what this means
-    EGLint bufferAge() const { return mBufferAge; }
-
-private:
-    Frame() {}
-    friend class EglManager;
-
-    EGLSurface mSurface;
-    EGLint mWidth;
-    EGLint mHeight;
-    EGLint mBufferAge;
-
-    // Maps from 0,0 in top-left to 0,0 in bottom-left
-    // If out is not an EGLint[4] you're going to have a bad time
-    void map(const SkRect& in, EGLint* out) const;
-};
 
 // This class contains the shared global EGL objects, such as EGLDisplay
 // and EGLConfig, which are re-used by CanvasContext
diff --git a/libs/hwui/renderthread/Frame.cpp b/libs/hwui/renderthread/Frame.cpp
new file mode 100644
index 0000000..126bb09
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Frame.h"
+#include <SkRect.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void Frame::map(const SkRect& in, int32_t* out) const {
+    /* The rectangles are specified relative to the bottom-left of the surface
+     * and the x and y components of each rectangle specify the bottom-left
+     * position of that rectangle.
+     *
+     * HWUI does everything with 0,0 being top-left, so need to map
+     * the rect
+     */
+    SkIRect idirty;
+    in.roundOut(&idirty);
+    int32_t y = mHeight - (idirty.y() + idirty.height());
+    // layout: {x, y, width, height}
+    out[0] = idirty.x();
+    out[1] = y;
+    out[2] = idirty.width();
+    out[3] = idirty.height();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h
new file mode 100644
index 0000000..99996fe
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+struct SkRect;
+typedef void *EGLSurface;
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class Frame {
+public:
+    Frame(int32_t width, int32_t height, int32_t bufferAge)
+            : mWidth(width)
+            , mHeight(height)
+            , mBufferAge(bufferAge) { }
+
+    int32_t width() const { return mWidth; }
+    int32_t height() const { return mHeight; }
+
+    // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+    // for what this means
+    int32_t bufferAge() const { return mBufferAge; }
+
+private:
+    Frame() {}
+    friend class EglManager;
+
+    int32_t mWidth;
+    int32_t mHeight;
+    int32_t mBufferAge;
+
+    EGLSurface mSurface;
+
+    // Maps from 0,0 in top-left to 0,0 in bottom-left
+    // If out is not an int32_t[4] you're going to have a bad time
+    void map(const SkRect& in, int32_t* out) const;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 0e4000b..45f6718 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include "FrameInfoVisualizer.h"
-#include "EglManager.h"
 
 #include <SkRect.h>
 #include <utils/RefBase.h>
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index 9dc2b59..df08599 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -18,6 +18,7 @@
 
 #include "DeferredLayerUpdater.h"
 #include "EglManager.h"
+#include "Frame.h"
 #include "ProfileRenderer.h"
 #include "renderstate/RenderState.h"
 #include "OpenGLReadback.h"
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4d239bc..68c04af 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -17,6 +17,7 @@
 #include "VulkanManager.h"
 
 #include "DeviceInfo.h"
+#include "Properties.h"
 #include "RenderThread.h"
 
 #include <GrContext.h>
@@ -100,6 +101,10 @@
     mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend,
             (GrBackendContext) mBackendContext.get()));
     DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
+
+    if (Properties::enablePartialUpdates && Properties::useBufferAge) {
+        mSwapBehavior = SwapBehavior::BufferAge;
+    }
 }
 
 // Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -162,7 +167,7 @@
     }
 
     // set up layout transfer from initial to color attachment
-    VkImageLayout layout = surface->mImageLayouts[backbuffer->mImageIndex];
+    VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
     SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
     VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
                                         VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
@@ -215,7 +220,7 @@
 
     // We need to notify Skia that we changed the layout of the wrapped VkImage
     GrVkImageInfo* imageInfo;
-    sk_sp<SkSurface> skSurface = surface->mSurfaces[backbuffer->mImageIndex];
+    sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
     skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
             SkSurface::kFlushRead_BackendHandleAccess);
     imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
@@ -243,10 +248,8 @@
 
     delete[] surface->mBackbuffers;
     surface->mBackbuffers = nullptr;
-    delete[] surface->mSurfaces;
-    surface->mSurfaces = nullptr;
-    delete[] surface->mImageLayouts;
-    surface->mImageLayouts = nullptr;
+    delete[] surface->mImageInfos;
+    surface->mImageInfos = nullptr;
     delete[] surface->mImages;
     surface->mImages = nullptr;
 }
@@ -286,8 +289,7 @@
     GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
 
     // set up initial image layouts and create surfaces
-    surface->mImageLayouts = new VkImageLayout[surface->mImageCount];
-    surface->mSurfaces = new sk_sp<SkSurface>[surface->mImageCount];
+    surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
     for (uint32_t i = 0; i < surface->mImageCount; ++i) {
         GrBackendRenderTargetDesc desc;
         GrVkImageInfo info;
@@ -306,9 +308,9 @@
         desc.fStencilBits = 0;
         desc.fRenderTargetHandle = (GrBackendObject) &info;
 
-        surface->mSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
+        VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
+        imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
                 desc, &props);
-        surface->mImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
     }
 
     SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -595,7 +597,7 @@
     VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
             surface->mCurrentBackbufferIndex;
     GrVkImageInfo* imageInfo;
-    SkSurface* skSurface = surface->mSurfaces[backbuffer->mImageIndex].get();
+    SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
     skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
             SkSurface::kFlushRead_BackendHandleAccess);
     // Check to make sure we never change the actually wrapped image
@@ -632,7 +634,7 @@
             0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
     mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
 
-    surface->mImageLayouts[backbuffer->mImageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+    surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
 
     // insert the layout transfer into the queue and wait on the acquire
     VkSubmitInfo submitInfo;
@@ -668,6 +670,20 @@
     mQueuePresentKHR(mPresentQueue, &presentInfo);
 
     surface->mBackbuffer.reset();
+    surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime;
+    surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false;
+    surface->mCurrentTime++;
+}
+
+int VulkanManager::getAge(VulkanSurface* surface) {
+    VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
+            surface->mCurrentBackbufferIndex;
+    if (mSwapBehavior == SwapBehavior::Discard
+            || surface->mImageInfos[backbuffer->mImageIndex].mInvalid) {
+        return 0;
+    }
+    uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed;
+    return surface->mCurrentTime - lastUsed;
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index f0e3320..d225b3f 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -46,6 +46,13 @@
         VkFence         mUsageFences[2];
     };
 
+    struct ImageInfo {
+        VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        sk_sp<SkSurface> mSurface;
+        uint16_t mLastUsed = 0;
+        bool mInvalid = true;
+    };
+
     sk_sp<SkSurface> mBackbuffer;
 
     VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
@@ -56,8 +63,8 @@
 
     uint32_t mImageCount;
     VkImage* mImages;
-    VkImageLayout* mImageLayouts;
-    sk_sp<SkSurface>* mSurfaces;
+    ImageInfo* mImageInfos;
+    uint16_t mCurrentTime = 0;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -87,6 +94,8 @@
     // VulkanSurface is passed into them so we just return true here.
     bool isCurrent(VulkanSurface* surface) { return true; }
 
+    int getAge(VulkanSurface* surface);
+
     // Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also
     // will transition the VkImage from a present layout to color attachment so that it can be used
     // by the client for drawing.
@@ -161,6 +170,12 @@
     uint32_t mPresentQueueIndex;
     VkQueue mPresentQueue = VK_NULL_HANDLE;
     VkCommandPool mCommandPool = VK_NULL_HANDLE;
+
+    enum class SwapBehavior {
+        Discard,
+        BufferAge,
+    };
+    SwapBehavior mSwapBehavior = SwapBehavior::Discard;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0ce598d..363b94a 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -118,7 +118,8 @@
 
     static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
         std::unique_ptr<Snapshot> snapshot(new Snapshot());
-        snapshot->clip(clip, SkRegion::kReplace_Op); // store clip first, so it isn't transformed
+        // store clip first, so it isn't transformed
+        snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom);
         *(snapshot->transform) = transform;
         return snapshot;
     }
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 8f2ba2d..45443b0 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -34,11 +34,11 @@
                 [](RenderProperties& props, Canvas& canvas) {
             canvas.save(SaveFlags::MatrixClip);
             {
-                canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+                canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
                 canvas.translate(100, 100);
                 canvas.rotate(45);
                 canvas.translate(-100, -100);
-                canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+                canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
                 canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
             }
             canvas.restore();
@@ -47,7 +47,7 @@
             {
                 SkPath clipCircle;
                 clipCircle.addCircle(100, 300, 100);
-                canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op);
+                canvas.clipPath(&clipCircle, kIntersect_SkClipOp);
                 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
             }
             canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 3630935..8b2852b 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -36,7 +36,7 @@
             // nested clipped saveLayers
             canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
-            canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
+            canvas.clipRect(50, 50, 350, 350, kIntersect_SkClipOp);
             canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
             canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 5ef8773..d44c3d5 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -82,7 +82,7 @@
                     int middleCount = canvas.save(SaveFlags::MatrixClip);
                     for (auto op : ops) {
                         int innerCount = canvas.save(SaveFlags::MatrixClip);
-                        canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
+                        canvas.clipRect(0, 0, cellSize, cellSize, kIntersect_SkClipOp);
                         canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
                         op(canvas, cellSize, paint);
                         canvas.restoreToCount(innerCount);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index bbaf267..3ef0d1c 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -177,7 +177,7 @@
         // Clip to padding
         // Can expect ~25% of views to have clip to padding with a non-null padding
         int clipRestoreCount = canvas->save(SaveFlags::MatrixClip);
-        canvas->clipRect(1, 1, 199, 199, SkRegion::kIntersect_Op);
+        canvas->clipRect(1, 1, 199, 199, kIntersect_SkClipOp);
 
         canvas->insertReorderBarrier(true);
 
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 0afabd8..7555fd4 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -23,7 +23,7 @@
 
 #include <gtest/gtest.h>
 #include <SkPath.h>
-#include <SkRegion.h>
+#include <SkClipOp.h>
 
 namespace android {
 namespace uirenderer {
@@ -68,13 +68,13 @@
     state.initializeSaveStack(200, 200,
             0, 0, 200, 200, Vector3());
 
-    state.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
+    state.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
 
-    state.clipRect(10, 10, 200, 200, SkRegion::kIntersect_Op);
+    state.clipRect(10, 10, 200, 200, kIntersect_SkClipOp);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
 
-    state.clipRect(50, 50, 150, 150, SkRegion::kReplace_Op);
+    state.clipRect(50, 50, 150, 150, kReplace_SkClipOp);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
 }
 
@@ -88,7 +88,7 @@
         // rotated clip causes complex clip
         state.rotate(10);
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+        state.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -97,7 +97,7 @@
     {
         // subtracted clip causes complex clip
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(50, 50, 150, 150, SkRegion::kDifference_Op);
+        state.clipRect(50, 50, 150, 150, kDifference_SkClipOp);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -108,7 +108,7 @@
         SkPath path;
         path.addOval(SkRect::MakeWH(200, 200));
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipPath(&path, SkRegion::kDifference_Op);
+        state.clipPath(&path, kDifference_SkClipOp);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -121,7 +121,7 @@
 
     state.save(SaveFlags::Clip);
     {
-        state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+        state.clipRect(0, 0, 10, 10, kIntersect_SkClipOp);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
     }
     state.restore();
@@ -145,7 +145,7 @@
 
     state.save(SaveFlags::Matrix); // NOTE: clip not saved
     {
-        state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+        state.clipRect(0, 0, 10, 10, kIntersect_SkClipOp);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
     }
     state.restore();
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index a1c225f..12622ff 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -174,7 +174,7 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
+        canvas.clipRect(200, 200, 400, 400, kIntersect_SkClipOp); // intersection should be empty
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
@@ -453,19 +453,19 @@
         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
 
         // left side clipped (to inset left half)
-        canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
+        canvas.clipRect(10, 0, 50, 100, kReplace_SkClipOp);
         canvas.drawBitmap(*bitmap, 0, 40, nullptr);
 
         // top side clipped (to inset top half)
-        canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
+        canvas.clipRect(0, 10, 100, 50, kReplace_SkClipOp);
         canvas.drawBitmap(*bitmap, 40, 0, nullptr);
 
         // right side clipped (to inset right half)
-        canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
+        canvas.clipRect(50, 0, 90, 100, kReplace_SkClipOp);
         canvas.drawBitmap(*bitmap, 80, 40, nullptr);
 
         // bottom not clipped, just abutting (inset bottom half)
-        canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
+        canvas.clipRect(0, 50, 100, 90, kReplace_SkClipOp);
         canvas.drawBitmap(*bitmap, 40, 70, nullptr);
     });
 
@@ -488,7 +488,7 @@
         SkPath path;
         path.addCircle(200, 200, 200, SkPath::kCW_Direction);
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipPath(&path, SkRegion::kIntersect_Op);
+        canvas.clipPath(&path, kIntersect_SkClipOp);
         SkPaint paint;
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setAntiAlias(true);
@@ -649,7 +649,7 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
+        canvas.clipRect(50, 50, 150, 150, kIntersect_SkClipOp);
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
@@ -973,7 +973,7 @@
         auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
+        canvas.clipRect(200, 200, 400, 400, kIntersect_SkClipOp);
         canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
 
         // draw within save layer may still be recorded, but shouldn't be drawn
@@ -1781,7 +1781,7 @@
     auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
             [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
         // Record time clip will be ignored by projectee
-        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
+        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
 
         canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(projectingRipple.get());
@@ -1993,7 +1993,7 @@
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // Apply a clip before the reorder barrier/shadow casting child is drawn.
         // This clip must be applied to the shadow cast by the child.
-        canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
+        canvas.clipRect(25, 25, 75, 75, kIntersect_SkClipOp);
         canvas.insertReorderBarrier(true);
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
@@ -2252,7 +2252,7 @@
     };
     auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
+        canvas.clipRect(0, -20, 10, 30, kReplace_SkClipOp);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
     });
 
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index dda432e..14fa5d6 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -57,7 +57,7 @@
 TEST(RecordingCanvas, clipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
+        canvas.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
         canvas.drawRect(0, 0, 50, 50, SkPaint());
         canvas.drawRect(50, 50, 100, 100, SkPaint());
         canvas.restore();
@@ -73,8 +73,8 @@
 TEST(RecordingCanvas, emptyClipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
-        canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op);
+        canvas.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
+        canvas.clipRect(100, 100, 200, 200, kIntersect_SkClipOp);
         canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
         canvas.restore();
     });
@@ -440,7 +440,7 @@
 TEST(RecordingCanvas, saveLayer_addClipFlag) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
+        canvas.clipRect(10, 20, 190, 180, kIntersect_SkClipOp);
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
@@ -459,7 +459,7 @@
 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, SkRegion::kReplace_Op);
+        canvas.clipRect(-1000, -1000, 1000, 1000, kReplace_SkClipOp);
 
         canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
@@ -549,7 +549,7 @@
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(0, -20); // avoid identity case
         // empty clip rect should force layer + contents to be rejected
-        canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op);
+        canvas.clipRect(0, -20, 200, -20, kIntersect_SkClipOp);
         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
@@ -568,7 +568,7 @@
     });
 
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
-        canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node
+        canvas.clipRect(0, 0, 0, 0, kIntersect_SkClipOp); // empty clip, reject node
         canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
     });
     ASSERT_TRUE(dl->isEmpty());
@@ -621,7 +621,7 @@
     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:
-        canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
+        canvas.clipRect(-100, -100, 300, 300, kIntersect_SkClipOp);
 
         SkPaint paint;
         paint.setColor(SK_ColorWHITE);
@@ -637,7 +637,7 @@
 TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op);
+        canvas.clipRect(-10, -10, 110, 110, kReplace_SkClipOp);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
         canvas.restore();
     });
@@ -675,7 +675,7 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
 
         // second chunk: no recorded clip, since inorder region
-        canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+        canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
         canvas.insertReorderBarrier(false);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index f4b686d..7c14952 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -469,7 +469,7 @@
     auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
             [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
         // Record time clip will be ignored by projectee
-        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
+        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
 
         canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(projectingRipple.get());
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index fa3e13d..598cd1e 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -93,8 +93,7 @@
 
     SkCanvas* prepareToDraw() {
         //mCanvas->reset(mSize.width(), mSize.height());
-        mCanvas->clipRect(0, 0, mSize.width(), mSize.height(),
-                               SkRegion::Op::kReplace_Op);
+        mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), kReplace_SkClipOp);
         return mCanvas->asSkCanvas();
     }
 
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index bb8eb2c..03f0b4d 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -55,12 +55,14 @@
 import java.util.Random;
 
 public class CaptivePortalLoginActivity extends Activity {
-    private static final String TAG = "CaptivePortalLogin";
+    private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
+    private static final boolean DBG = true;
+
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
     private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
 
-    private URL mURL;
+    private URL mUrl;
     private Network mNetwork;
     private CaptivePortal mCaptivePortal;
     private NetworkCallback mNetworkCallback;
@@ -72,17 +74,18 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mCm = ConnectivityManager.from(this);
-        String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
-        if (url == null) url = mCm.getCaptivePortalServerUrl();
-        try {
-            mURL = new URL(url);
-        } catch (MalformedURLException e) {
-            // System misconfigured, bail out in a way that at least provides network access.
-            Log.e(TAG, "Invalid captive portal URL, url=" + url);
-            done(Result.WANTED_AS_IS);
-        }
         mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
         mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
+        mUrl = getUrl();
+        if (mUrl == null) {
+            // getUrl() failed to parse the url provided in the intent: bail out in a way that
+            // at least provides network access.
+            done(Result.WANTED_AS_IS);
+            return;
+        }
+        if (DBG) {
+            Log.d(TAG, String.format("onCreate for %s", mUrl.toString()));
+        }
 
         // Also initializes proxy system properties.
         mCm.bindProcessToNetwork(mNetwork);
@@ -149,6 +152,9 @@
     }
 
     private void done(Result result) {
+        if (DBG) {
+            Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString()));
+        }
         if (mNetworkCallback != null) {
             mCm.unregisterNetworkCallback(mNetworkCallback);
             mNetworkCallback = null;
@@ -185,22 +191,31 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        int id = item.getItemId();
-        if (id == R.id.action_use_network) {
-            done(Result.WANTED_AS_IS);
-            return true;
+        final Result result;
+        final String action;
+        final int id = item.getItemId();
+        switch (id) {
+            case R.id.action_use_network:
+                result = Result.WANTED_AS_IS;
+                action = "USE_NETWORK";
+                break;
+            case R.id.action_do_not_use_network:
+                result = Result.UNWANTED;
+                action = "DO_NOT_USE_NETWORK";
+                break;
+            default:
+                return super.onOptionsItemSelected(item);
         }
-        if (id == R.id.action_do_not_use_network) {
-            done(Result.UNWANTED);
-            return true;
+        if (DBG) {
+            Log.d(TAG, String.format("onOptionsItemSelect %s for %s", action, mUrl.toString()));
         }
-        return super.onOptionsItemSelected(item);
+        done(result);
+        return true;
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-
         if (mNetworkCallback != null) {
             mCm.unregisterNetworkCallback(mNetworkCallback);
             mNetworkCallback = null;
@@ -215,10 +230,27 @@
                 } catch (InterruptedException e) {
                 }
             }
-            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL.toString())));
+            final String url = mUrl.toString();
+            if (DBG) {
+                Log.d(TAG, "starting activity with intent ACTION_VIEW for " + url);
+            }
+            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
         }
     }
 
+    private URL getUrl() {
+        String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
+        if (url == null) {
+            url = mCm.getCaptivePortalServerUrl();
+        }
+        try {
+            return new URL(url);
+        } catch (MalformedURLException e) {
+            Log.e(TAG, "Invalid captive portal URL " + url);
+        }
+        return null;
+    }
+
     private void testForCaptivePortal() {
         new Thread(new Runnable() {
             public void run() {
@@ -230,7 +262,7 @@
                 HttpURLConnection urlConnection = null;
                 int httpResponseCode = 500;
                 try {
-                    urlConnection = (HttpURLConnection) mURL.openConnection();
+                    urlConnection = (HttpURLConnection) mUrl.openConnection();
                     urlConnection.setInstanceFollowRedirects(false);
                     urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
                     urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
@@ -292,7 +324,7 @@
                 // settings.  Now prompt the WebView read the Network-specific proxy settings.
                 setWebViewProxy();
                 // Load the real page.
-                view.loadUrl(mURL.toString());
+                view.loadUrl(mUrl.toString());
                 return;
             } else if (mPagesLoaded == 2) {
                 // Prevent going back to empty first page.
diff --git a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
index a7c9105..edcea0e 100644
--- a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
@@ -24,6 +24,7 @@
           android:name=".SampleTrustAgent"
           android:label="@string/app_name"
           android:permission="android.permission.BIND_TRUST_AGENT"
+          android:directBootAware="true"
           android:exported="true">
         <intent-filter>
           <action android:name="android.service.trust.TrustAgentService" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/layout-v26/sample_trust_agent_settings.xml b/packages/Keyguard/test/SampleTrustAgent/res/layout-v26/sample_trust_agent_settings.xml
new file mode 100644
index 0000000..4669971
--- /dev/null
+++ b/packages/Keyguard/test/SampleTrustAgent/res/layout-v26/sample_trust_agent_settings.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    <Button android:id="@+id/enable_trust"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Grant trust for 30 seconds" />
+    <Button android:id="@+id/revoke_trust"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Revoke trust" />
+    <Button android:id="@+id/crash"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Crash" />
+    <CheckBox android:id="@+id/managing_trust"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Managing trust" />
+    <CheckBox android:id="@+id/managing_trust_direct_boot"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Managing trust direct boot"/>
+
+    <CheckBox android:id="@+id/report_unlock_attempts"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Report unlock attempts" />
+    <CheckBox android:id="@+id/report_device_locked"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Report device locked or unlocked" />
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+        <Button android:id="@+id/check_device_locked"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Device locked?" />
+        <TextView android:id="@+id/check_device_locked_result"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_tile_text.xml b/packages/Keyguard/test/SampleTrustAgent/res/xml-v26/sample_trust_agent.xml
similarity index 67%
rename from packages/SystemUI/res/color/qs_tile_text.xml
rename to packages/Keyguard/test/SampleTrustAgent/res/xml-v26/sample_trust_agent.xml
index 90e0bce..26d5aa0 100644
--- a/packages/SystemUI/res/color/qs_tile_text.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/res/xml-v26/sample_trust_agent.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2015 The Android Open Source Project
+  ~ Copyright (C) 2014 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -15,8 +14,6 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
-    <item android:color="#B3FFFFFF" /> <!-- 70% white -->
-</selector>
\ No newline at end of file
+<trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
+             android:settingsActivity=".SampleTrustAgentSettings"
+             android:unlockProfile="true" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml b/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml
index b363ab4..6cd34bb 100644
--- a/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml
@@ -15,4 +15,4 @@
   ~ limitations under the License
   -->
 <trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
-        android:settingsActivity=".SampleTrustAgentSettings" />
+             android:settingsActivity=".SampleTrustAgentSettings" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index b8f16e7..4b50cf8 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.os.PersistableBundle;
+import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.service.trust.TrustAgentService;
 import android.support.v4.content.LocalBroadcastManager;
@@ -57,26 +58,47 @@
             = "preference.report_unlock_attempts";
     private static final String PREFERENCE_MANAGING_TRUST
             = "preference.managing_trust";
+    private static final String PREFERENCE_MANAGING_TRUST_DIRECT_BOOT
+            = "preference.managing_trust_direct_boot";
     private static final String PREFERENCE_REPORT_DEVICE_LOCKED = "preference.report_device_locked";
 
     private static final String TAG = "SampleTrustAgent";
 
+    private static final BroadcastReceiver mUnlockReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+        }
+    };
+
+    private boolean mIsUserUnlocked;
+
     @Override
     public void onCreate() {
         super.onCreate();
+        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+        mIsUserUnlocked = um.isUserUnlocked();
+        Log.i(TAG,, "onCreate, is user unlocked=" + mIsUserUnlocked);
         mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_GRANT_TRUST);
         filter.addAction(ACTION_REVOKE_TRUST);
+        if (!mIsUserUnlocked) {
+            filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+        }
         mLocalBroadcastManager.registerReceiver(mReceiver, filter);
         if (ALLOW_EXTERNAL_BROADCASTS) {
             registerReceiver(mReceiver, filter);
         }
 
-        setManagingTrust(getIsManagingTrust(this));
-        PreferenceManager.getDefaultSharedPreferences(this)
-                .registerOnSharedPreferenceChangeListener(this);
+        if (!mIsUserUnlocked) {
+            boolean trustManaged = getIsManagingTrustDirectBoot(this);
+            Log.i(TAG, "in Direct boot." + (trustManaged ? "manage" : "cannot manage") + "trust");
+            setManagingTrust(getIsManagingTrustDirectBoot(this));
+        } else {
+            onBootCompleted();
+        }
     }
 
     @Override
@@ -137,6 +159,12 @@
                 .unregisterOnSharedPreferenceChangeListener(this);
     }
 
+    private void onBootCompleted() {
+        PreferenceManager.getDefaultSharedPreferences(this)
+                .registerOnSharedPreferenceChangeListener(this);
+        setManagingTrust(getIsManagingTrust(this));
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -158,6 +186,9 @@
                 }
             } else if (ACTION_REVOKE_TRUST.equals(action)) {
                 revokeTrust();
+            } else if (intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                Log.d(TAG, "User unlocked and boot completed.");
+                onBootCompleted();
             }
         }
     };
@@ -203,6 +234,7 @@
     public static void setIsManagingTrust(Context context, boolean enabled) {
         SharedPreferences sharedPreferences = PreferenceManager
                 .getDefaultSharedPreferences(context);
+        Log.d("AAAA", "save manage trust preference. Enabled=" + enabled);
         sharedPreferences.edit().putBoolean(PREFERENCE_MANAGING_TRUST, enabled).apply();
     }
 
@@ -212,6 +244,21 @@
         return sharedPreferences.getBoolean(PREFERENCE_MANAGING_TRUST, false);
     }
 
+    public static void setIsManagingTrustDirectBoot(Context context, boolean enabled) {
+        Context directBootContext = context.createDeviceProtectedStorageContext();
+        SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(directBootContext);
+        Log.d("AAAA", "save to direct boot preference. Enabled=" + enabled);
+        sharedPreferences.edit().putBoolean(PREFERENCE_MANAGING_TRUST_DIRECT_BOOT, enabled).apply();
+    }
+
+    public static boolean getIsManagingTrustDirectBoot(Context context) {
+        Context directBootContext = context.createDeviceProtectedStorageContext();
+        SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(directBootContext);
+        return sharedPreferences.getBoolean(PREFERENCE_MANAGING_TRUST_DIRECT_BOOT, false);
+    }
+
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
         if (PREFERENCE_MANAGING_TRUST.equals(key)) {
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
index 29b15cb..1b17169 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
@@ -33,6 +33,7 @@
     private CheckBox mReportUnlockAttempts;
     private CheckBox mReportDeviceLocked;
     private CheckBox mManagingTrust;
+    private CheckBox mManagingTrustDirectBoot;
     private TextView mCheckDeviceLockedResult;
 
     private KeyguardManager mKeyguardManager;
@@ -59,6 +60,8 @@
 
         mManagingTrust = (CheckBox) findViewById(R.id.managing_trust);
         mManagingTrust.setOnCheckedChangeListener(this);
+        mManagingTrustDirectBoot = (CheckBox) findViewById(R.id.managing_trust_direct_boot);
+        mManagingTrustDirectBoot.setOnCheckedChangeListener(this);
 
         mCheckDeviceLockedResult = (TextView) findViewById(R.id.check_device_locked_result);
     }
@@ -68,6 +71,8 @@
         super.onResume();
         mReportUnlockAttempts.setChecked(SampleTrustAgent.getReportUnlockAttempts(this));
         mManagingTrust.setChecked(SampleTrustAgent.getIsManagingTrust(this));
+        mManagingTrustDirectBoot.setChecked(
+            SampleTrustAgent.getIsManagingTrustDirectBoot(this));
         updateTrustedState();
     }
 
@@ -94,6 +99,8 @@
             SampleTrustAgent.setIsManagingTrust(this, isChecked);
         } else if (buttonView == mReportDeviceLocked) {
             SampleTrustAgent.setReportDeviceLocked(this, isChecked);
+        } else if (buttonView == mManagingTrustDirectBoot) {
+            SampleTrustAgent.setIsManagingTrustDirectBoot(this, isChecked);
         }
     }
 
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 5808b30..235ff3d 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -32,7 +32,7 @@
     <string name="template_page_range" msgid="428638530038286328">"Mfululizo wa <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
     <string name="pages_range_example" msgid="8558694453556945172">"k.m. 1–5, 8, 11–13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Chungulia kwanza kabla ya kuchapisha"</string>
-    <string name="install_for_print_preview" msgid="6366303997385509332">"Sakinisha kitazamaji cha PDF kwa onyesho la kuchungulia"</string>
+    <string name="install_for_print_preview" msgid="6366303997385509332">"Sakinisha Kifungua PDF ili uweze kuchungulia"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Programu ya kuchapisha imeacha kufanya kazi"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"Inaleta kazi ya kuchapisha"</string>
     <string name="save_as_pdf" msgid="5718454119847596853">"Hifadhi kama PDF"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 222cc5c..3e6c3f2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -119,7 +119,7 @@
             // and twice max user count for system and secure.
             final int size = 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers();
             try {
-                mBackingStore = new MemoryIntArray(size, false);
+                mBackingStore = new MemoryIntArray(size);
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Created backing store " + mBackingStore);
                 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 461573f..cbeb878 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -243,8 +243,8 @@
                 return lines;
             }
             try {
-                final Cursor cursor = provider.query(resolveCallingPackage(), uri, null, null, null,
-                        null, null);
+                final Cursor cursor = provider.query(resolveCallingPackage(), uri, null, null,
+                        null);
                 try {
                     while (cursor != null && cursor.moveToNext()) {
                         lines.add(cursor.getString(1) + "=" + cursor.getString(2));
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
index f653371..d54e33f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -26,7 +26,7 @@
 
     public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_BUTTON";
 
-    public static final int VERSION = 1;
+    public static final int VERSION = 2;
 
     /**
      * Returns a view in the nav bar.  If the id is set "back", "home", "recent_apps", "menu",
@@ -38,7 +38,6 @@
      * Interface for button actions.
      */
     interface ButtonInterface {
-        void setImageResource(@DrawableRes int resId);
 
         void setImageDrawable(@Nullable Drawable drawable);
 
@@ -47,5 +46,7 @@
         void setVertical(boolean vertical);
 
         void setCarMode(boolean carMode);
+
+        void setDarkIntensity(float intensity);
     }
 }
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_dark.png
new file mode 100644
index 0000000..0a6074b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 0000000..64bc40a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..fb44f22a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_dark.png
new file mode 100644
index 0000000..a665e23
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 0000000..4f65660
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 0000000..c57face
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..bbaab44
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..dba0040
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..2b64b80
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..151a3c0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-land/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..3456a97
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_dark.png
new file mode 100644
index 0000000..5383215
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 0000000..3a22912
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..c7e4731
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_dark.png
new file mode 100644
index 0000000..d2949f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 0000000..06e4c26
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 0000000..1c855c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 0000000..371a84d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 0000000..360ed77
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..96cecc9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_dark.png
new file mode 100644
index 0000000..9ff9825
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 0000000..e505f5e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 0000000..472c55b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 0000000..c04d650
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 0000000..0f6d206
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..58bf920
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_dark.png
new file mode 100644
index 0000000..f47533ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 0000000..f299d97
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 0000000..84d6dc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_dark.png
new file mode 100644
index 0000000..d813bc7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_dark.png
new file mode 100644
index 0000000..30e8782
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked_dark.png
new file mode 100644
index 0000000..83fc662
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_dark.png
new file mode 100644
index 0000000..71101a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_menu_dark.png
new file mode 100644
index 0000000..4a477ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_menu_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_dark.png
new file mode 100644
index 0000000..bc24c00
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_dark.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml b/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
index 24ac018..604e918 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
@@ -22,6 +22,6 @@
         android:pathData="m18.250000,12.000000a6.250000,6.250000 0.000000,1.000000 1.000000,-12.500000 0.000000,6.250000 6.250000,0.000000 1.000000,1.000000 12.500000,0.000000z"
         android:fillColor="?android:attr/colorPrimary" />
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?android:attr/colorControlNormal"
         android:pathData="M20.000000,8.700000L20.000000,4.000000L15.300000,4.000000L12.000000,0.700000 8.700000,4.000000L4.000000,4.000000L4.000000,8.700000L0.700000,12.000000 4.000000,15.300000L4.000000,20.000000L8.700000,20.000000L12.000000,23.299999 15.300000,20.000000L20.000000,20.000000L20.000000,15.300000L23.299999,12.000000 20.000000,8.700000zM12.000000,18.000000C8.700000,18.000000 6.000000,15.300000 6.000000,12.000000 6.000000,8.700000 8.700000,6.000000 12.000000,6.000000c3.300000,0.000000 6.000000,2.700000 6.000000,6.000000 0.000000,3.300000 -2.700000,6.000000 -6.000000,6.000000zM12.000000,8.000000c-2.200000,0.000000 -4.000000,1.800000 -4.000000,4.000000 0.000000,2.200000 1.800000,4.000000 4.000000,4.000000 2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000 0.000000,-2.200000 -1.800000,-4.000000 -4.000000,-4.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
index 4d9ca88..3345f61 100644
--- a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
+++ b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml
@@ -21,5 +21,5 @@
         android:viewportHeight="24.0">
     <path
         android:pathData="M20.000000,5.000000L4.000000,5.000000C2.900000,5.000000 2.000000,5.900000 2.000000,7.000000l0.000000,10.000000c0.000000,1.100000 0.900000,2.000000 2.000000,2.000000l16.000000,0.000000c1.100000,0.000000 2.000000,-0.900000 2.000000,-2.000000L22.000000,7.000000C22.000000,5.900000 21.100000,5.000000 20.000000,5.000000zM11.000000,8.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,8.000000zM11.000000,11.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,11.000000zM8.000000,8.000000l2.000000,0.000000l0.000000,2.000000L8.000000,10.000000L8.000000,8.000000zM8.000000,11.000000l2.000000,0.000000l0.000000,2.000000L8.000000,13.000000L8.000000,11.000000zM7.000000,13.000000L5.000000,13.000000l0.000000,-2.000000l2.000000,0.000000L7.000000,13.000000zM7.000000,10.000000L5.000000,10.000000L5.000000,8.000000l2.000000,0.000000L7.000000,10.000000zM16.000000,17.000000L8.000000,17.000000l0.000000,-2.000000l8.000000,0.000000L16.000000,17.000000zM16.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L16.000000,13.000000zM16.000000,10.000000l-2.000000,0.000000L14.000000,8.000000l2.000000,0.000000L16.000000,10.000000zM19.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L19.000000,13.000000zM19.000000,10.000000l-2.000000,0.000000L17.000000,8.000000l2.000000,0.000000L19.000000,10.000000z"
-        android:fillColor="@color/navigation_bar_icon_color"/>
+        android:fillColor="?attr/singleToneColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml b/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml
index f901e86..49ee48b 100644
--- a/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml
@@ -20,7 +20,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="icon"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
index 994cf8c..5aeceba 100644
--- a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
@@ -20,7 +20,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="icon"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate.xml
index bc545ba..8b2585b 100644
--- a/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate.xml
@@ -18,7 +18,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_screen_rotation_48px_outlines"
         android:translateX="24"
diff --git a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml
index 0bd75d1..603d0bf 100644
--- a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml
@@ -18,7 +18,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_screen_rotation_48px_outlines"
         android:translateX="24"
diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
index 0ac6795..17185a7c 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
@@ -18,7 +18,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="icon"
         android:translateX="24"
diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
index 0cca6e3..88bf486 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
@@ -18,7 +18,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="icon"
         android:translateX="24"
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
index 0c65389..8a3e975 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
index b9a315c..1dadc05 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_detail_empty.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_detail_empty.xml
index dd92126..435bb9b 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_detail_empty.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_detail_empty.xml
@@ -17,7 +17,8 @@
         android:width="56dp"
         android:height="56dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="@color/qs_detail_empty"
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
index 0cb1f32..36eb418 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#4DFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
index 9a68dad..2e9e741 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
index 8051795..0fdbe1f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#4DFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
index 794eb9e..d2e9eb2 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml
index 28d2e26..164a557 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml
@@ -18,7 +18,8 @@
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
     android:alpha=".3"
-    android:width="64dp" >
+    android:width="64dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
index f4c20a9..7e2eca8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M24.0,4.0C12.95,4.0 4.0,12.95 4.0,24.0s8.95,20.0 20.0,20.0 20.0,-8.95 20.0,-20.0S35.05,4.0 24.0,4.0zm10.0,22.0L14.0,26.0l0.0,-4.0l20.0,0.0l0.0,4.0z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
index fb26c09..5f4fa11 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
@@ -17,7 +17,8 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
similarity index 61%
rename from packages/SystemUI/res/drawable/qs_dual_tile_caret.xml
rename to packages/SystemUI/res/drawable/ic_qs_network_logging.xml
index 71400db..1340ae1 100644
--- a/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_network_logging.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2014 The Android Open Source Project
+Copyright (C) 2016 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -13,13 +13,17 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
 
+<!-- Placeholder icon for network logging until the real icon is finalized-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:width="12.0dp"
+  android:height="12.0dp"
+  android:viewportWidth="24.0"
+  android:viewportHeight="24.0"
+  android:tint="#4DFFFFFF" >
     <path
-        android:fillColor="@color/qs_tile_text"
-        android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
+      android:fillColor="#FFFFFFFF"
+      android:pathData="M7,18v-2h6v2H7z M7,14v-2h10v2H7z M8.5,9 12,5.5 15.5,9 13,9 13,13 11,13 11,9z"/>
+
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml b/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
index 778ccbc..b99dc03 100644
--- a/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
@@ -18,10 +18,11 @@
     android:height="64dp"
     android:viewportWidth="24"
     android:viewportHeight="24"
-    android:alpha="0.3">
+    android:alpha="0.3"
+    android:tint="?android:attr/colorControlNormal">
 
     <path
-        android:fillColor="#FFF"
+        android:fillColor="#FFFFFF"
         android:pathData="M6,12c0,5.5,4.5,10,10,10c1,0,2-0.2,3-0.5c-4.1-1.3-7-5.1-7-9.5s2.9-8.3,7-9.5C18.1,2.2,17.1,2,16,2C10.5,2,6,6.5,6,12z" />
 
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml b/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
index aaca663..d875592 100644
--- a/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
@@ -17,10 +17,11 @@
     android:width="64dp"
     android:height="64dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal">
 
     <path
-        android:fillColor="#FFF"
+        android:fillColor="#FFFFFF"
         android:pathData="M6,12c0,5.5,4.5,10,10,10c1,0,2-0.2,3-0.5c-4.1-1.3-7-5.1-7-9.5s2.9-8.3,7-9.5C18.1,2.2,17.1,2,16,2C10.5,2,6,6.5,6,12z" />
 
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml
index bd46012..2d831b2 100644
--- a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml
@@ -17,7 +17,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:fillColor="#4DFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml b/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
index b78d3bf..0673848 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
@@ -18,7 +18,8 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillAlpha="0.3"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
index e055de7..fbf9e71 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
@@ -18,7 +18,8 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M10.0,14.6l-8.0,8.0l8.0,0.0l0,-8z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
index 71c40df..3a55623 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
@@ -17,7 +17,8 @@
         android:width="16.0dp"
         android:height="32dp"
         android:viewportWidth="12.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M3.500000,11.000000L1.800000,11.000000L1.800000,4.400000L0.200000,5.100000L0.200000,3.700000l3.100000,-1.300000l0.200000,0.000000L3.500000,11.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_2.xml b/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
index 8a48817..e9f5a0b 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
@@ -18,7 +18,8 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M14.0,10.6l-12.0,12.0l12.0,0.0L14.0,10.6z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
index 39cc94c..769d648 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
@@ -18,7 +18,8 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillAlpha="0.3"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
index e9a57ea..ddd8065 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
@@ -17,7 +17,8 @@
         android:width="17.333334dp"
         android:height="32dp"
         android:viewportWidth="13.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,6.000000l0.800000,0.000000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000s0.200000,-0.500000 0.200000,-0.900000c0.000000,-0.300000 -0.100000,-0.600000 -0.200000,-0.800000S3.200000,3.700000 2.900000,3.700000C2.700000,3.700000 2.500000,3.800000 2.300000,4.000000S2.100000,4.400000 2.100000,4.700000L0.500000,4.700000C0.500000,4.000000 0.700000,3.400000 1.100000,3.000000s1.000000,-0.600000 1.700000,-0.600000c0.800000,0.000000 1.400000,0.200000 1.900000,0.600000s0.700000,1.000000 0.700000,1.800000c0.000000,0.400000 -0.100000,0.700000 -0.300000,1.100000S4.600000,6.500000 4.300000,6.600000C4.700000,6.800000 5.000000,7.100000 5.200000,7.400000s0.300000,0.700000 0.300000,1.200000c0.000000,0.800000 -0.200000,1.400000 -0.700000,1.800000s-1.100000,0.700000 -1.900000,0.700000c-0.700000,0.000000 -1.300000,-0.200000 -1.800000,-0.600000s-0.700000,-1.000000 -0.700000,-1.800000L2.000000,8.700000C2.000000,9.000000 2.100000,9.300000 2.300000,9.500000s0.400000,0.300000 0.600000,0.300000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000S3.900000,9.000000 3.900000,8.600000c0.000000,-0.500000 -0.100000,-0.800000 -0.300000,-1.000000S3.200000,7.300000 2.800000,7.300000L2.000000,7.300000L2.000000,6.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
index 012e95e..1bec1b8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
@@ -18,7 +18,8 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
index 42045d1..8e1f8eb 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
@@ -17,7 +17,8 @@
         android:width="16.0dp"
         android:height="32dp"
         android:viewportWidth="12.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.600000,7.800000l0.700000,0.000000l0.000000,1.300000L4.600000,9.100000L4.600000,11.000000L3.000000,11.000000L3.000000,9.200000L0.100000,9.200000L0.000000,8.100000L3.000000,2.500000l1.700000,0.000000L4.700000,7.800000zM1.600000,7.800000L3.000000,7.800000l0.000000,-3.000000L2.900000,5.000000L1.600000,7.800000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
index 4d7f325..e0c6b68 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
index 96e2fd4..1c068e5 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
@@ -17,7 +17,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:name="dot1"
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
index 4f253e3..0a85392 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M21.799999,22.299999l-1.199999,-1.299999 0.000000,0.000000 -9.600000,-10.000000 0.000000,0.000000 -6.400000,-6.700000 -1.300000,1.300000 6.400000,6.700000 -8.700000,8.700000 16.900000,0.000000 2.600000,2.700001z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
index e49a409..4c90421 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
@@ -17,7 +17,8 @@
         android:width="6.6666665dp"
         android:height="32dp"
         android:viewportWidth="5.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml
index 326373d..db4df76 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml
index 8baa4eb..6e5439d 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.0,22.0l20.0,0.0 0.0,-20.0z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml
index bf19a71..194edc3 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml
index 01839e85..b57af5c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml
index 48151ad..7d754a8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
index 9d42a44..64aadf9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
@@ -17,7 +17,8 @@
         android:width="9.333333dp"
         android:height="32dp"
         android:viewportWidth="7.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
index f509d71..31918a9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
@@ -17,7 +17,8 @@
         android:width="8.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
index 236fdac..4122b76 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
@@ -17,7 +17,8 @@
         android:width="6.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
index b7242e6..8766075 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
@@ -17,7 +17,8 @@
         android:width="17.333334dp"
         android:height="32dp"
         android:viewportWidth="13.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,9.700000l2.000000,0.000000L4.000000,11.000000L0.300000,11.000000L0.300000,2.500000L2.000000,2.500000L2.000000,9.700000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
index 3af0629..5ff7d85 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml b/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml
index f7fd97c..4e65004 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml
@@ -18,7 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000L22.000000,2.000000L2.000000,22.000000zM20.000000,20.000000L6.800000,20.000000L20.000000,6.800000L20.000000,20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
index c510972..a3823ae 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
@@ -17,7 +17,8 @@
         android:width="6.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_r.xml b/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
index 66f64c9..2c10dc3f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
@@ -17,7 +17,8 @@
         android:width="8.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
index e6f9292..fe963b1 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M13.8,12.2l5.7,0.0L23.6,7.0C23.2,6.7 18.7,3.0 12.0,3.0C5.3,3.0 0.8,6.7 0.4,7.0L12.0,21.5l0.0,0.0l0.0,0.0l1.8,-2.2L13.8,12.2z"
         android:fillAlpha="0.3"
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
index d423ccb..82d2be2 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M13.8,13.2c-0.1,0.0 -0.3,-0.1 -0.4,-0.1c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,0.0 -0.6,-0.1 -0.9,-0.1c0.0,0.0 0.0,0.0 -0.1,0.0c0.0,0.0 0.0,0.0 0.0,0.0s0.0,0.0 0.0,0.0c0.0,0.0 0.0,0.0 -0.1,0.0c-0.3,0.0 -0.6,0.0 -0.9,0.1c-0.1,0.0 -0.3,0.0 -0.4,0.1c-0.2,0.0 -0.3,0.1 -0.5,0.1c-0.2,0.0 -0.3,0.1 -0.5,0.1c-0.1,0.0 -0.1,0.0 -0.2,0.1c-1.6,0.5 -2.7,1.3 -2.8,1.5l5.3,6.6l0.0,0.0l0.0,0.0l0.0,0.0l0.0,0.0l1.8,-2.2L13.700002,13.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
index 1982130..f30ba76 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M13.8,12.2l4.9,0.0c-1.0,-0.7 -3.4,-2.2 -6.7,-2.2c-4.1,0.0 -6.9,2.2 -7.2,2.5l7.2,9.0l0.0,0.0l0.0,0.0l1.8,-2.2L13.800001,12.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
index b350111..8a17083 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M13.8,12.2l5.7,0.0l1.0,-1.2C20.0,10.6 16.8,8.0 12.0,8.0s-8.0,2.6 -8.5,3.0L12.0,21.5l0.0,0.0l0.0,0.0l1.8,-2.2L13.8,12.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
index 136a004..fcd57d0 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:pathData="M13.8,12.2l5.7,0.0L23.6,7.0C23.2,6.7 18.7,3.0 12.0,3.0C5.3,3.0 0.8,6.7 0.4,7.0L12.0,21.5l0.0,0.0l0.0,0.0l1.8,-2.2L13.8,12.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml
index ad6b247..a7889ab 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml
@@ -17,7 +17,8 @@
         android:width="56dp"
         android:height="56dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorControlNormal">
 
     <path
         android:pathData="M24.0,4.0C15.0,4.0 6.7,7.0 0.0,12.0l24.0,32.0l24.0,-32.0C41.3,7.0 33.0,4.0 24.0,4.0z"
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
index c505783..bbff2e1 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M17.500000,16.500000L5.800000,3.400000c0.000000,0.000000 0.000000,0.000000 0.000000,0.000000l-2.700000,-3.000000L1.600000,1.800000l2.200000,2.500000c-2.000000,1.000000 -3.200000,2.000000 -3.400000,2.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l3.200000,-3.900000l2.400000,2.700000l1.500000,-1.400000L17.500000,16.500000L17.500000,16.500000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
index 2dcdb71..071892a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
@@ -17,7 +17,8 @@
         android:width="26.0dp"
         android:height="24.0dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M21.0,8.5
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
index 1bc7438..c0e1037 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
index 5856115..f609295 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.100000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.500000,6.500000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
index 4a5e1f8..f44b303 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
index 965442d..850f5b9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
index b29d3f9..8ccc1fd9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml
index 3d58869..45cfc1c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml
@@ -17,7 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,2.000000C7.700000,2.000000 3.700000,3.900000 0.400000,6.400000L13.000000,22.000000L25.600000,6.500000C22.299999,4.000000 18.299999,2.000000 13.000000,2.000000zM13.000000,18.600000L3.300000,7.000000l0.000000,0.000000l0.000000,0.000000C6.000000,5.300000 8.700000,4.000000 13.000000,4.000000s7.000000,1.400000 9.700000,3.000000l0.000000,0.000000l0.000000,0.000000L13.000000,18.600000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
index 35844b7..542797a 100644
--- a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
@@ -20,7 +20,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_signal_flashlight"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
index d53608f..a5ba05b 100644
--- a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
@@ -20,7 +20,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_signal_flashlight"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_disable.xml b/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
index 439851d..e36f270 100644
--- a/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
@@ -20,7 +20,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_signal_location"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_enable.xml b/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
index 8614458..46a72bc 100644
--- a/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
@@ -20,7 +20,8 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48" >
+    android:viewportWidth="48"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_signal_location"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm.xml b/packages/SystemUI/res/drawable/ic_volume_alarm.xml
index a8ca0d6..e64f445 100644
--- a/packages/SystemUI/res/drawable/ic_volume_alarm.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_alarm.xml
@@ -17,10 +17,11 @@
     android:height="24.0dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal">
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M44.0,11.44l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-28.24,-4.66l-2.57,-3.06 -9.19,7.71 2.57,3.06 9.19,-7.71zm9.24,9.22l-3.0,0.0l0.0,12.0l9.49,5.71 1.51,-2.47 -8.0,-4.74l0.0,-10.5zm-1.01,-8.0c-9.95,0.0 -17.99,8.06 -17.99,18.0s8.04,18.0 17.99,18.0 18.01,-8.06 18.01,-18.0 -8.06,-18.0 -18.01,-18.0zm0.01,32.0c-7.73,0.0 -14.0,-6.27 -14.0,-14.0s6.27,-14.0 14.0,-14.0 14.0,6.27 14.0,14.0 -6.26,14.0 -14.0,14.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
index 8e2f083..37d7690 100644
--- a/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
@@ -17,10 +17,11 @@
     android:height="24.0dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M24.0,12.0c7.73,0.0 14.0,6.27 14.0,14.0 0.0,1.69 -0.31,3.3 -0.86,4.8l3.04,3.04c1.16,-2.37 1.82,-5.03 1.82,-7.84 0.0,-9.94 -8.06,-18.0 -18.01,-18.0 -2.81,0.0 -5.46,0.66 -7.84,1.81l3.05,3.05c1.5,-0.55 3.11,-0.86 4.8,-0.86zm20.0,-0.56l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-38.16,-6.85l-2.55,2.54 2.66,2.66 -2.22,1.86 2.84,2.84 2.22,-1.86 1.6,1.6c-2.73,3.16 -4.39,7.27 -4.39,11.77 0.0,9.94 8.04,18.0 17.99,18.0 4.51,0.0 8.62,-1.67 11.77,-4.4l4.4,4.4 2.54,-2.55 -34.91,-34.91 -1.95,-1.95zm27.1,32.19c-2.43,2.01 -5.54,3.22 -8.94,3.22 -7.73,0.0 -14.0,-6.27 -14.0,-14.0 0.0,-3.4 1.21,-6.51 3.22,-8.94l19.72,19.72zm-16.91,-30.23l-2.84,-2.84 -1.7,1.43 2.84,2.84 1.7,-1.43z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
index 71df4d3..5c3c650 100644
--- a/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
@@ -17,10 +17,11 @@
     android:height="24.0dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M29.41,19.0L34.0,14.41L34.0,22.0l1.0,0.0l5.71,-5.71 -4.3,-4.29 4.29,-4.29L35.0,2.0l-1.0,0.0l0.0,7.59L29.41,5.0 28.0,6.41 33.59,12.0 28.0,17.59 29.41,19.0zM36.0,5.83l1.88,1.88L36.0,9.59L36.0,5.83zm0.0,8.58l1.88,1.88L36.0,18.17l0.0,-3.76zM40.0,31.0c-2.49,0.0 -4.89,-0.4 -7.14,-1.14 -0.69,-0.22 -1.48,-0.06 -2.0,0.49l-4.4,4.41c-5.67,-2.88 -10.29,-7.51 -13.18,-13.17l4.4,-4.41c0.55,-0.5 0.71,-1.3 0.49,-2.03C17.4,12.9 17.0,10.49 17.0,8.0c0.0,-1.11 -0.89,-2.0 -2.0,-2.0L8.0,6.0c-1.11,0.0 -2.0,0.89 -2.0,2.0 0.0,18.78 15.22,34.0 34.0,34.0 1.11,0.0 2.0,-0.89 2.0,-2.0l0.0,-7.0c0.0,-1.11 -0.89,-2.0 -2.0,-2.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
index dc6d301..0f488a4 100644
--- a/packages/SystemUI/res/drawable/ic_volume_collapse.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
@@ -18,7 +18,8 @@
     android:height="24dp"
     android:viewportHeight="24"
     android:viewportWidth="24"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <group
         android:name="chevron_02"
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml
index a60623f..70ff1f3 100644
--- a/packages/SystemUI/res/drawable/ic_volume_expand.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_expand.xml
@@ -18,7 +18,8 @@
     android:height="24dp"
     android:viewportHeight="24"
     android:viewportWidth="24"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <group
         android:name="chevron_01"
diff --git a/packages/SystemUI/res/drawable/ic_volume_media.xml b/packages/SystemUI/res/drawable/ic_volume_media.xml
index 97089f1..d689207 100644
--- a/packages/SystemUI/res/drawable/ic_volume_media.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_media.xml
@@ -17,10 +17,11 @@
     android:height="24.0dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M12.0,3.0l0.0,9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12.0 6.0,14.01 6.0,16.5S8.01,21.0 10.5,21.0c2.31,0.0 4.2,-1.75 4.45,-4.0L15.0,17.0L15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_bt.xml b/packages/SystemUI/res/drawable/ic_volume_media_bt.xml
index 3364d9c..9b7b2da 100644
--- a/packages/SystemUI/res/drawable/ic_volume_media_bt.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_media_bt.xml
@@ -17,13 +17,14 @@
     android:width="24.0dp"
     android:height="24.0dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/colorControlNormal">
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M17.0,3.0l-7.0,0.0l0.0,9.3C9.5,12.1 9.0,12.0 8.5,12.0C6.0,12.0 4.0,14.0 4.0,16.5S6.0,21.0 8.5,21.0s4.5,-2.3 4.5,-4.5C13.0,14.7 13.0,6.0 13.0,6.0l4.0,0.0L17.0,3.0z"/>
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M23.4,9.9L20.5,7.0L20.0,7.0l0.0,3.8l-2.3,-2.3L17.0,9.2l2.8,2.8L17.0,14.8l0.7,0.7l2.3,-2.3L20.0,17.0l0.5,0.0l2.8,-2.8L21.2,12.0L23.4,9.9zM21.0,8.9l0.9,0.9l-0.9,1.0L21.0,8.9zM21.9,14.2L21.0,15.1l0.0,-1.9L21.9,14.2z"/>
 
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml
index 39f54f1..17ac01d 100644
--- a/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml
@@ -17,16 +17,17 @@
     android:height="24dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal">
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M13.0,6.0l4.0,0.0L17.0,3.0l-7.0,0.0l0.0,5.6l3.0,3.0C13.0,8.8 13.0,6.0 13.0,6.0z"/>
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M2.1,5.7L8.4,12.0C6.0,12.1 4.0,14.0 4.0,16.5S6.0,21.0 8.5,21.0c2.7,0.0 4.5,-2.3 4.5,-4.3l0.0,-0.1l3.9,3.9l1.3,-1.3L3.4,4.5L2.1,5.7z"/>
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M23.4,9.9L20.5,7.0L20.0,7.0l0.0,3.8l-2.3,-2.3L17.0,9.2l2.8,2.8L17.0,14.8l0.7,0.7l2.3,-2.3L20.0,17.0l0.5,0.0l2.8,-2.8L21.2,12.0L23.4,9.9zM21.0,8.9l0.9,0.9l-0.9,1.0L21.0,8.9zM21.9,14.2L21.0,15.1l0.0,-1.9L21.9,14.2z"/>
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
index beb806c..267d09d 100644
--- a/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
@@ -17,13 +17,14 @@
     android:height="24.0dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0l0.0,5.6l3.0,3.0C15.0,8.8 15.0,6.0 15.0,6.0z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M4.8,3.9L3.5,5.1l6.9,6.9C8.0,12.1 6.0,14.0 6.0,16.5C6.0,19.0 8.0,21.0 10.5,21.0c2.7,0.0 4.5,-2.3 4.5,-4.3c0.0,0.0 0.0,-0.1 0.0,-0.1l4.0,4.0l1.3,-1.3L4.8,3.9z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote.xml b/packages/SystemUI/res/drawable/ic_volume_remote.xml
index b363178..fe396d9 100644
--- a/packages/SystemUI/res/drawable/ic_volume_remote.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_remote.xml
@@ -17,10 +17,11 @@
     android:height="24dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM38.0,14.0L10.0,14.0l0.0,3.3c7.9,2.6 14.2,8.8 16.7,16.7L38.0,34.0L38.0,14.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0zM42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml b/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
index 5f39ad7..2e05122 100644
--- a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
@@ -17,22 +17,23 @@
     android:height="24.0dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M23.7,21.3l-1.1,-1.0c0.0,0.0 0.0,0.0 0.0,0.0L21.0,18.8l0.0,0.0L5.8,5.0l0.0,0.0L3.6,3.0l0.0,0.0L1.7,1.3L0.3,2.7l1.1,1.0C1.2,4.1 1.0,4.5 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.2L18.2,19.0L14.0,19.0l0.0,2.0l6.4,0.0l1.9,1.7L23.7,21.3z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M21.0,5.0l0.0,11.1l2.0,1.8L23.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0L6.6,3.0l2.2,2.0L21.0,5.0z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer.xml b/packages/SystemUI/res/drawable/ic_volume_ringer.xml
index c566d5a..18af711 100644
--- a/packages/SystemUI/res/drawable/ic_volume_ringer.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer.xml
@@ -17,10 +17,11 @@
     android:height="24dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
index 0c20361..bc926c3 100644
--- a/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
@@ -17,10 +17,11 @@
     android:height="24dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M23.000000,44.000000c2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000l-8.000000,0.000000C19.000000,42.200001 20.799999,44.000000 23.000000,44.000000zM36.000000,21.000000c0.000000,-6.100000 -4.300000,-11.300000 -10.000000,-12.600000L26.000000,7.000000c0.000000,-1.700000 -1.300000,-3.000000 -3.000000,-3.000000c-1.700000,0.000000 -3.000000,1.300000 -3.000000,3.000000l0.000000,1.400000c-1.000000,0.200000 -2.000000,0.600000 -2.900000,1.100000L36.000000,28.400000L36.000000,21.000000zM35.500000,38.000000l4.000000,4.000000l2.500000,-2.500000L8.500000,6.000000L6.000000,8.500000l5.800000,5.800000C10.700000,16.299999 10.000000,18.600000 10.000000,21.000000l0.000000,11.000000l-4.000000,4.000000l0.000000,2.000000L35.500000,38.000000z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
index 38b3234..ffbffad 100644
--- a/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
@@ -17,10 +17,11 @@
     android:height="24dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24dp" >
+    android:width="24dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_settings.xml b/packages/SystemUI/res/drawable/ic_volume_settings.xml
deleted file mode 100644
index 9f79b5a..0000000
--- a/packages/SystemUI/res/drawable/ic_volume_settings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="20dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0"
-    android:width="20dp" >
-
-    <path
-        android:fillColor="@color/volume_settings_icon_color"
-        android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z" />
-
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_system.xml b/packages/SystemUI/res/drawable/ic_volume_system.xml
index ccd8e18..71eed34 100644
--- a/packages/SystemUI/res/drawable/ic_volume_system.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_system.xml
@@ -17,16 +17,17 @@
     android:height="24.0dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M17.7,14.8l-4.5,-2.3c-0.2,-0.1 -0.4,-0.1 -0.5,-0.1l-0.8,0.0l0.0,-6.0c0.0,-0.8 -0.7,-1.5 -1.5,-1.5S8.9,5.6 8.9,6.4l0.0,10.7l-3.4,-0.7c-0.1,0.0 -0.2,0.0 -0.2,0.0c-0.3,0.0 -0.6,0.1 -0.8,0.3l-0.8,0.8l4.9,4.9c0.3,0.3 0.6,0.4 1.1,0.4l6.8,0.0c0.8,0.0 1.3,-0.6 1.4,-1.3l0.8,-5.3c0.0,-0.1 0.0,-0.1 0.0,-0.2C18.6,15.5 18.2,15.0 17.7,14.8z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M14.3,8.8l1.8,0.9c1.5,-2.0 1.4,-4.8 -0.4,-6.6l-1.4,1.4C15.5,5.7 15.5,7.6 14.3,8.8z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M17.9,10.6l1.8,0.9C22.0,8.0 21.6,3.3 18.5,0.3l-1.4,1.4C19.5,4.1 19.8,7.9 17.9,10.6z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_system_mute.xml b/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
index dfcb655..baad92a 100644
--- a/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
@@ -17,19 +17,20 @@
     android:height="24.0dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M14.3,8.8l1.8,0.9c1.5,-2.0 1.4,-4.8 -0.4,-6.6l-1.4,1.4C15.5,5.7 15.5,7.6 14.3,8.8z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M17.9,10.6l1.8,0.9C22.0,8.0 21.6,3.3 18.5,0.3l-1.4,1.4C19.5,4.1 19.8,7.9 17.9,10.6z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M22.0,20.6l-3.5,-2.8l0.0,0.0l-9.6,-7.6l0.0,0.0L3.2,5.7L2.0,7.3l6.9,5.4L8.9,17.0l-3.4,-0.7c-0.1,0.0 -0.2,0.0 -0.2,0.0c-0.3,0.0 -0.6,0.1 -0.8,0.3l-0.8,0.8l4.9,4.9c0.3,0.3 0.6,0.4 1.1,0.4l6.8,0.0c0.8,0.0 1.3,-0.6 1.4,-1.3l0.2,-1.5l2.6,2.1L22.0,20.6z" />
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M11.9,6.4c0.0,-0.8 -0.7,-1.5 -1.5,-1.5S8.9,5.6 8.9,6.4l0.0,1.5l3.0,2.4L11.9,6.4z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_voice.xml b/packages/SystemUI/res/drawable/ic_volume_voice.xml
index 133253e..7dc3dac 100644
--- a/packages/SystemUI/res/drawable/ic_volume_voice.xml
+++ b/packages/SystemUI/res/drawable/ic_volume_voice.xml
@@ -17,10 +17,11 @@
     android:height="24.0dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24.0dp" >
+    android:width="24.0dp"
+    android:tint="?android:attr/colorControlNormal" >
 
     <path
-        android:fillColor="@color/volume_icon_color"
+        android:fillColor="#FFFFFF"
         android:pathData="M13.25,21.59c2.88,5.66 7.51,10.29 13.18,13.17l4.4,-4.41c0.55,-0.55 1.34,-0.71 2.03,-0.49C35.1,30.6 37.51,31.0 40.0,31.0c1.11,0.0 2.0,0.89 2.0,2.0l0.0,7.0c0.0,1.11 -0.89,2.0 -2.0,2.0C21.22,42.0 6.0,26.78 6.0,8.0c0.0,-1.1 0.9,-2.0 2.0,-2.0l7.0,0.0c1.11,0.0 2.0,0.89 2.0,2.0 0.0,2.4 0.4,4.9 1.14,7.1 0.2,0.6 0.06,1.48 -0.49,2.03l-4.4,4.42z" />
 
 </vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/back.xml b/packages/SystemUI/res/layout/back.xml
index d256622..4e8726b 100644
--- a/packages/SystemUI/res/layout/back.xml
+++ b/packages/SystemUI/res/layout/back.xml
@@ -21,7 +21,6 @@
     android:layout_width="@dimen/navigation_key_width"
     android:layout_height="match_parent"
     android:layout_weight="0"
-    android:src="@drawable/ic_sysbar_back"
     systemui:keyCode="4"
     android:scaleType="center"
     android:contentDescription="@string/accessibility_back"
diff --git a/packages/SystemUI/res/layout/home.xml b/packages/SystemUI/res/layout/home.xml
index f11592d..9586327 100644
--- a/packages/SystemUI/res/layout/home.xml
+++ b/packages/SystemUI/res/layout/home.xml
@@ -20,7 +20,6 @@
     android:layout_width="@dimen/navigation_key_width"
     android:layout_height="match_parent"
     android:layout_weight="0"
-    android:src="@drawable/ic_sysbar_home"
     systemui:keyCode="3"
     android:scaleType="center"
     android:contentDescription="@string/accessibility_home"
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 90b74d0..5e85ba0 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -25,7 +25,6 @@
         android:layout_width="@dimen/navigation_extra_key_width"
         android:layout_height="match_parent"
         android:layout_marginEnd="2dp"
-        android:src="@drawable/ic_sysbar_menu"
         android:scaleType="centerInside"
         systemui:keyCode="82"
         android:visibility="invisible"
@@ -36,7 +35,6 @@
         android:layout_width="@dimen/navigation_extra_key_width"
         android:layout_height="match_parent"
         android:layout_marginEnd="2dp"
-        android:src="@drawable/ic_ime_switcher_default"
         android:visibility="invisible"
         android:contentDescription="@string/accessibility_ime_switch_button"
         android:scaleType="centerInside"
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index cce9c0f..5b0f0df 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -22,7 +22,7 @@
      <TextView android:id="@+id/tile_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textColor="@color/qs_tile_text"
+            android:textColor="?android:attr/textColorSecondary"
             android:gravity="center_horizontal"
             android:minLines="2"
             android:padding="0dp"
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 53baf74..8667a5a 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -39,4 +39,16 @@
         android:src="@drawable/ic_qs_vpn"
         android:visibility="invisible" />
 
-</RelativeLayout>
\ No newline at end of file
+    <!-- Only shown if both images are visible -->
+    <ImageView
+        android:id="@+id/footer_icon2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="8dp"
+        android:layout_toStartOf="@id/footer_icon"
+        android:contentDescription="@null"
+        android:src="@drawable/ic_qs_network_logging"
+        android:visibility="invisible" />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/recent_apps.xml b/packages/SystemUI/res/layout/recent_apps.xml
index eb8ee43c..870bcf7 100644
--- a/packages/SystemUI/res/layout/recent_apps.xml
+++ b/packages/SystemUI/res/layout/recent_apps.xml
@@ -21,7 +21,6 @@
     android:layout_width="@dimen/navigation_key_width"
     android:layout_height="match_parent"
     android:layout_weight="0"
-    android:src="@drawable/ic_sysbar_recent"
     android:scaleType="center"
     android:contentDescription="@string/accessibility_recent"
     android:paddingStart="@dimen/navigation_key_padding"
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 73f26e2..e4ea08e 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -42,7 +42,7 @@
             android:singleLine="true"
             android:ellipsize="start"
             android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
-            android:imeOptions="actionNone|flagNoExtractUi|flagNoFullscreen" />
+            android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
 
     <FrameLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 52565ba..e41991a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -131,7 +131,6 @@
     <color name="light_mode_icon_color_dual_tone_background">#4dffffff</color>
     <color name="light_mode_icon_color_dual_tone_fill">#ffffff</color>
 
-    <color name="volume_icon_color">#ffffffff</color>
     <color name="volume_settings_icon_color">#7fffffff</color>
     <color name="volume_slider_inactive">@*android:color/quaternary_device_default_settings</color>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 29b5b62..a7d4aa0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -81,6 +81,9 @@
     <!-- Height of a heads up notification in the status bar -->
     <dimen name="notification_max_heads_up_height">148dp</dimen>
 
+    <!-- a threshold in dp per second that is considered fast scrolling -->
+    <dimen name="scroll_fast_threshold">1500dp</dimen>
+
     <!-- Height of a the shelf with the notification icons -->
     <dimen name="notification_shelf_height">32dp</dimen>
 
@@ -94,6 +97,9 @@
     <!-- The amount the content shifts upwards when transforming into the icon -->
     <dimen name="notification_icon_transform_content_shift">32dp</dimen>
 
+    <!-- The padding on the bottom of the notifications on the keyguard -->
+    <dimen name="keyguard_indication_bottom_padding">12sp</dimen>
+
     <!-- Minimum layouted height of a notification in the statusbar-->
     <dimen name="min_notification_layout_height">48dp</dimen>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 78fc9f9..0f9e940 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -127,7 +127,7 @@
     <style name="TextAppearance.StatusBar.Expanded.Clock">
         <item name="android:textSize">@dimen/qs_time_expanded_size</item>
         <item name="android:fontFamily">sans-serif-medium</item>
-        <item name="android:textColor">#ffffff</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.Date">
@@ -159,7 +159,7 @@
 
     <style name="TextAppearance.QS">
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">@color/qs_text</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index e036128..10328a4 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -96,9 +97,10 @@
                 dismissCallbackRegistry);
     }
 
-    public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
-            View headsUpScrim, LockscreenWallpaper lockscreenWallpaper) {
-        return new ScrimController(scrimBehind, scrimInFront, headsUpScrim);
+    public ScrimController createScrimController(LightBarController lightBarController,
+            ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
+            LockscreenWallpaper lockscreenWallpaper) {
+        return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim);
     }
 
     public VolumeDialogController createVolumeDialogController(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index c6dde46..09671e7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -156,6 +156,10 @@
         @Override
         public void onListenerRegistered(IPinnedStackController controller) {
             mPinnedStackController = controller;
+
+            // Update the controller with the current tuner state
+            setMinimizedState(mIsMinimized);
+            setSnapToEdge(mEnableSnapToEdge);
         }
 
         @Override
@@ -353,10 +357,13 @@
      */
     private void setSnapToEdge(boolean snapToEdge) {
         mSnapAlgorithm.setSnapToEdge(snapToEdge);
-        try {
-            mPinnedStackController.setSnapToEdge(snapToEdge);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not set snap mode to edge", e);
+
+        if (mPinnedStackController != null) {
+            try {
+                mPinnedStackController.setSnapToEdge(snapToEdge);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not set snap mode to edge", e);
+            }
         }
     }
 
@@ -365,10 +372,13 @@
      */
     private void setMinimizedState(boolean isMinimized) {
         mIsMinimized = isMinimized;
-        try {
-            mPinnedStackController.setIsMinimized(isMinimized);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not set minimized state", e);
+
+        if (mPinnedStackController != null) {
+            try {
+                mPinnedStackController.setIsMinimized(isMinimized);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not set minimized state", e);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 43308de..f3da47b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -51,6 +51,7 @@
     private final View mRootView;
     private final TextView mFooterText;
     private final ImageView mFooterIcon;
+    private final ImageView mFooterIcon2;
     private final Context mContext;
     private final Callback mCallback = new Callback();
 
@@ -62,8 +63,11 @@
 
     private boolean mIsVisible;
     private boolean mIsIconVisible;
+    private boolean mIsIcon2Visible;
     private CharSequence mFooterTextContent = null;
+    private int mFooterTextId;
     private int mFooterIconId;
+    private int mFooterIcon2Id;
 
     public QSFooter(QSPanel qsPanel, Context context) {
         mRootView = LayoutInflater.from(context)
@@ -71,7 +75,9 @@
         mRootView.setOnClickListener(this);
         mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
         mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
+        mFooterIcon2 = (ImageView) mRootView.findViewById(R.id.footer_icon2);
         mFooterIconId = R.drawable.ic_qs_vpn;
+        mFooterIcon2Id = R.drawable.ic_qs_network_logging;
         mContext = context;
         mMainHandler = new Handler(Looper.getMainLooper());
     }
@@ -119,7 +125,10 @@
     }
 
     private void handleRefreshState() {
-        mIsIconVisible = mSecurityController.isVpnEnabled();
+        boolean isVpnEnabled = mSecurityController.isVpnEnabled();
+        boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
+        mIsIconVisible = isVpnEnabled || isNetworkLoggingEnabled;
+        mIsIcon2Visible = isVpnEnabled && isNetworkLoggingEnabled;
         if (mSecurityController.isDeviceManaged()) {
             final CharSequence organizationName =
                     mSecurityController.getDeviceOwnerOrganizationName();
@@ -131,12 +140,21 @@
                         mContext.getResources().getString(R.string.do_disclosure_generic);
             }
             mIsVisible = true;
+            int footerIconId = isVpnEnabled
+                    ? R.drawable.ic_qs_vpn
+                    : R.drawable.ic_qs_network_logging;
+            if (mFooterIconId != footerIconId) {
+                mFooterIconId = footerIconId;
+                mMainHandler.post(mUpdateIcon);
+            }
         } else {
             boolean isBranded = mSecurityController.isVpnBranded();
             mFooterTextContent = mContext.getResources().getText(
                     isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer);
             // Update the VPN footer icon, if needed.
-            int footerIconId = isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn;
+            int footerIconId = isVpnEnabled
+                    ? (isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn)
+                    : R.drawable.ic_qs_network_logging;
             if (mFooterIconId != footerIconId) {
                 mFooterIconId = footerIconId;
                 mMainHandler.post(mUpdateIcon);
@@ -258,6 +276,7 @@
         @Override
         public void run() {
             mFooterIcon.setImageResource(mFooterIconId);
+            mFooterIcon2.setImageResource(mFooterIcon2Id);
         }
     };
 
@@ -269,6 +288,7 @@
             }
             mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
             mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE);
+            mFooterIcon2.setVisibility(mIsIcon2Visible ? View.VISIBLE : View.INVISIBLE);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index af1823c..d5a6a58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -399,7 +399,7 @@
         RecentsActivityLaunchState launchState = config.getLaunchState();
         if (!loadPlan.hasTasks()) {
             loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
-                    !launchState.launchedFromHome);
+                    !launchState.launchedFromHome && !launchState.launchedViaDockGesture);
         }
 
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
@@ -526,12 +526,14 @@
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
         MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
 
-        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
-        // through SystemUI, we need to reset the config launch flags to ensure that we do not
-        // wait on the system to send a signal that was never queued.
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        launchState.reset();
+        if (!isChangingConfigurations()) {
+            // Workaround for b/22542869, if the RecentsActivity is started again, but without going
+            // through SystemUI, we need to reset the config launch flags to ensure that we do not
+            // wait on the system to send a signal that was never queued.
+            RecentsConfiguration config = Recents.getConfiguration();
+            RecentsActivityLaunchState launchState = config.getLaunchState();
+            launchState.reset();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index d46d267..cf8d332 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -103,7 +103,7 @@
     private boolean mDimmed;
     private boolean mDark;
 
-    private int mBgTint = 0;
+    private int mBgTint = NO_COLOR;
     private float mBgAlpha = 1f;
 
     /**
@@ -507,8 +507,10 @@
      * Sets the tint color of the background
      */
     public void setTintColor(int color, boolean animated) {
-        mBgTint = color;
-        updateBackgroundTint(animated);
+        if (color != mBgTint) {
+            mBgTint = color;
+            updateBackgroundTint(animated);
+        }
     }
 
     /**
@@ -567,13 +569,15 @@
     }
 
     private void setBackgroundTintColor(int color) {
-        mCurrentBackgroundTint = color;
-        if (color == mNormalColor) {
-            // We don't need to tint a normal notification
-            color = 0;
+        if (color != mCurrentBackgroundTint) {
+            mCurrentBackgroundTint = color;
+            if (color == mNormalColor) {
+                // We don't need to tint a normal notification
+                color = 0;
+            }
+            mBackgroundDimmed.setTint(color);
+            mBackgroundNormal.setTint(color);
         }
-        mBackgroundDimmed.setTint(color);
-        mBackgroundNormal.setTint(color);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 661cc3c..e89fe55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -193,7 +193,7 @@
     private View mChildAfterViewWhenDismissed;
     private View mGroupParentWhenDismissed;
     private boolean mRefocusOnDismiss;
-    private float mIconTransformationAmount;
+    private float mContentTransformationAmount;
     private boolean mIconsVisible = true;
     private boolean mAboveShelf;
     private boolean mIsLastChild;
@@ -837,23 +837,29 @@
     /**
      * Set how much this notification is transformed into an icon.
      *
-     * @param iconTransformationAmount A value from 0 to 1 indicating how much we are transformed
-     *                                 to an icon
+     * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
+     *                                 to the content away
      * @param isLastChild is this the last child in the list. If true, then the transformation is
      *                    different since it's content fades out.
      */
-    public void setIconTransformationAmount(float iconTransformationAmount, boolean isLastChild) {
+    public void setContentTransformationAmount(float contentTransformationAmount,
+            boolean isLastChild) {
         boolean changeTransformation = isLastChild != mIsLastChild;
-        changeTransformation |= mIconTransformationAmount != iconTransformationAmount;
+        changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
         mIsLastChild = isLastChild;
-        mIconTransformationAmount = iconTransformationAmount;
+        mContentTransformationAmount = contentTransformationAmount;
         if (changeTransformation) {
             updateContentTransformation();
-            boolean iconsVisible = mIconTransformationAmount == 0.0f;
-            if (iconsVisible != mIconsVisible) {
-                mIconsVisible = iconsVisible;
-                updateIconVisibilities();
-            }
+        }
+    }
+
+    /**
+     * Set the icons to be visible of this notification.
+     */
+    public void setIconsVisible(boolean iconsVisible) {
+        if (iconsVisible != mIconsVisible) {
+            mIconsVisible = iconsVisible;
+            updateIconVisibilities();
         }
     }
 
@@ -864,9 +870,9 @@
 
     private void updateContentTransformation() {
         float contentAlpha;
-        float translationY = - mIconTransformationAmount * mIconTransformContentShift;
+        float translationY = -mContentTransformationAmount * mIconTransformContentShift;
         if (mIsLastChild) {
-            contentAlpha = 1.0f - mIconTransformationAmount;
+            contentAlpha = 1.0f - mContentTransformationAmount;
             contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
             contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
             translationY *= 0.4f;
@@ -885,7 +891,9 @@
     }
 
     private void updateIconVisibilities() {
-        boolean visible = isChildInGroup() || isBelowSpeedBump() || mIconsVisible;
+        boolean visible = isChildInGroup()
+                || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
+                || mIconsVisible;
         mPublicLayout.setIconsVisible(visible);
         mPrivateLayout.setIconsVisible(visible);
         if (mChildrenContainer != null) {
@@ -1679,13 +1687,17 @@
 
     @Override
     public void setClipBottomAmount(int clipBottomAmount) {
-        super.setClipBottomAmount(clipBottomAmount);
-        mPrivateLayout.setClipBottomAmount(clipBottomAmount);
-        mPublicLayout.setClipBottomAmount(clipBottomAmount);
-        if (mGuts != null) {
-            mGuts.setClipBottomAmount(clipBottomAmount);
+        if (clipBottomAmount != mClipBottomAmount) {
+            super.setClipBottomAmount(clipBottomAmount);
+            mPrivateLayout.setClipBottomAmount(clipBottomAmount);
+            mPublicLayout.setClipBottomAmount(clipBottomAmount);
+            if (mGuts != null) {
+                mGuts.setClipBottomAmount(clipBottomAmount);
+            }
         }
         if (mChildrenContainer != null) {
+            // We have to update this even if it hasn't changed, since the children locations can
+            // have changed
             mChildrenContainer.setClipBottomAmount(clipBottomAmount);
         }
     }
@@ -1871,8 +1883,8 @@
         }
 
         @Override
-        protected void onYTranslationAnimationFinished() {
-            super.onYTranslationAnimationFinished();
+        protected void onYTranslationAnimationFinished(View view) {
+            super.onYTranslationAnimationFinished(view);
             if (mHeadsupDisappearRunning) {
                 setHeadsUpAnimatingAway(false);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 3687f6d..a6e730d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -208,6 +208,13 @@
                 expandedIcon = null;
                 throw new IconException("Couldn't create icon: " + ic);
             }
+            expandedIcon.setVisibility(View.INVISIBLE);
+            expandedIcon.setOnVisibilityChangedListener(
+                    newVisibility -> {
+                        if (row != null) {
+                            row.setIconsVisible(newVisibility != View.VISIBLE);
+                        }
+                    });
         }
 
         public void setIconTag(int key, Object tag) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 680562a..e054bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -33,9 +34,7 @@
 import com.android.systemui.statusbar.stack.ExpandableViewState;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackScrollState;
-
-import java.util.ArrayList;
-import java.util.WeakHashMap;
+import com.android.systemui.statusbar.stack.ViewState;
 
 /**
  * A notification shelf view that is placed inside the notification scroller. It manages the
@@ -43,10 +42,12 @@
  */
 public class NotificationShelf extends ActivatableNotificationView {
 
+    public static final boolean SHOW_AMBIENT_ICONS = true;
+    private static final boolean USE_ANIMATIONS_WHEN_OPENING =
+            SystemProperties.getBoolean("debug.icon_opening_animations", true);
     private ViewInvertHelper mViewInvertHelper;
     private boolean mDark;
     private NotificationIconContainer mShelfIcons;
-    private ArrayList<StatusBarIconView> mIcons = new ArrayList<>();
     private ShelfState mShelfState;
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
@@ -60,6 +61,7 @@
     private int mNotGoneIndex;
     private boolean mHasItemsInStableShelf;
     private NotificationIconContainer mCollapsedIcons;
+    private int mScrollFastThreshold;
 
     public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -101,6 +103,8 @@
         setLayoutParams(layoutParams);
         int padding = getResources().getDimensionPixelOffset(R.dimen.shelf_icon_container_padding);
         mShelfIcons.setPadding(padding, 0, padding, 0);
+        mScrollFastThreshold = getResources().getDimensionPixelOffset(
+                R.dimen.scroll_fast_threshold);
     }
 
     @Override
@@ -162,6 +166,7 @@
                 mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
             }
             mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
+            mShelfState.hidden = !mAmbientState.isShadeExpanded();
         } else {
             mShelfState.hidden = true;
             mShelfState.location = ExpandableViewState.LOCATION_GONE;
@@ -174,15 +179,15 @@
      * the icons from the notification area into the shelf.
      */
     public void updateAppearance() {
-        WeakHashMap<View, NotificationIconContainer.IconState> iconStates =
-                mShelfIcons.resetViewStates();
+        mShelfIcons.resetViewStates();
+        float shelfStart = getTranslationY();
         float numViewsInShelf = 0.0f;
         View lastChild = mAmbientState.getLastVisibleBackgroundChild();
         mNotGoneIndex = -1;
         float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
         float expandAmount = 0.0f;
-        if (getTranslationY() >= interpolationStart) {
-            expandAmount = (getTranslationY() - interpolationStart) / getIntrinsicHeight();
+        if (shelfStart >= interpolationStart) {
+            expandAmount = (shelfStart - interpolationStart) / getIntrinsicHeight();
             expandAmount = Math.min(1.0f, expandAmount);
         }
         //  find the first view that doesn't overlap with the shelf
@@ -196,6 +201,12 @@
         int colorTwoBefore = NO_COLOR;
         int previousColor = NO_COLOR;
         float transitionAmount = 0.0f;
+        boolean scrollingFast = mAmbientState.getCurrentScrollVelocity() > mScrollFastThreshold
+                || (mAmbientState.isExpansionChanging()
+                        && Math.abs(mAmbientState.getExpandingVelocity()) > mScrollFastThreshold);
+        boolean expandingAnimated = mAmbientState.isExpansionChanging()
+                && !mAmbientState.isPanelTracking();
+        int baseZHeight = mAmbientState.getBaseZHeight();
         while (notificationIndex < mHostLayout.getChildCount()) {
             ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex);
             notificationIndex++;
@@ -204,30 +215,28 @@
                 continue;
             }
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            StatusBarIconView icon = row.getEntry().expandedIcon;
-            NotificationIconContainer.IconState iconState = iconStates.get(icon);
             float notificationClipEnd;
-            float shelfStart = getTranslationY();
-            boolean aboveShelf = row.getTranslationZ() > mAmbientState.getBaseZHeight();
+            boolean aboveShelf = row.getTranslationZ() > baseZHeight;
             boolean isLastChild = child == lastChild;
+            float rowTranslationY = row.getTranslationY();
             if (isLastChild || aboveShelf || backgroundForceHidden) {
                 notificationClipEnd = shelfStart + getIntrinsicHeight();
             } else {
                 notificationClipEnd = shelfStart - mPaddingBetweenElements;
-                float height = notificationClipEnd - row.getTranslationY();
+                float height = notificationClipEnd - rowTranslationY;
                 if (!row.isBelowSpeedBump() && height <= getNotificationMergeSize()) {
                     // We want the gap to close when we reached the minimum size and only shrink
                     // before
                     notificationClipEnd = Math.min(shelfStart,
-                            row.getTranslationY() + getNotificationMergeSize());
+                            rowTranslationY + getNotificationMergeSize());
                 }
             }
             updateNotificationClipHeight(row, notificationClipEnd);
-            float inShelfAmount = updateIconAppearance(row, iconState, icon, expandAmount,
-                    isLastChild);
+            float inShelfAmount = updateIconAppearance(row, expandAmount, scrollingFast,
+                    expandingAnimated, isLastChild);
             numViewsInShelf += inShelfAmount;
             int ownColorUntinted = row.getBackgroundColorWithoutTint();
-            if (row.getTranslationY() >= getTranslationY() && mNotGoneIndex == -1) {
+            if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) {
                 mNotGoneIndex = notGoneIndex;
                 setTintColor(previousColor);
                 setOverrideTintColor(colorTwoBefore, transitionAmount);
@@ -248,11 +257,9 @@
             notGoneIndex++;
             previousColor = ownColorUntinted;
         }
+        mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
         mShelfIcons.calculateIconTranslations();
         mShelfIcons.applyIconStates();
-        setVisibility(numViewsInShelf != 0.0f && mAmbientState.isShadeExpanded()
-                ? VISIBLE
-                : INVISIBLE);
         boolean hideBackground = numViewsInShelf < 1.0f;
         setHideBackground(hideBackground || backgroundForceHidden);
         if (mNotGoneIndex == -1) {
@@ -275,41 +282,116 @@
     /**
      * @return the icon amount how much this notification is in the shelf;
      */
-    private float updateIconAppearance(ExpandableNotificationRow row,
-            NotificationIconContainer.IconState iconState, StatusBarIconView icon,
-            float expandAmount, boolean isLastChild) {
+    private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount,
+            boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
         // Let calculate how much the view is in the shelf
         float viewStart = row.getTranslationY();
-        int transformHeight = row.getActualHeight() + mPaddingBetweenElements;
+        int fullHeight = row.getActualHeight() + mPaddingBetweenElements;
+        float iconTransformDistance = getIntrinsicHeight() * 1.5f;
         if (isLastChild) {
-            transformHeight =
-                    Math.min(transformHeight, row.getMinHeight() - getIntrinsicHeight());
+            fullHeight = Math.min(fullHeight, row.getMinHeight() - getIntrinsicHeight());
+            iconTransformDistance = Math.min(iconTransformDistance, row.getMinHeight()
+                    - getIntrinsicHeight());
         }
-        float viewEnd = viewStart + transformHeight;
-        float iconAppearAmount;
-        float yTranslation;
-        float alpha = 1.0f;
-        if (viewEnd >= getTranslationY() && (mAmbientState.isShadeExpanded()
+        float viewEnd = viewStart + fullHeight;
+        float fullTransitionAmount;
+        float iconTransitionAmount;
+        float shelfStart = getTranslationY();
+        if (viewEnd >= shelfStart && (mAmbientState.isShadeExpanded()
                 || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
-            if (viewStart < getTranslationY()) {
-                float linearAmount = (getTranslationY() - viewStart) / transformHeight;
+            if (viewStart < shelfStart) {
+
+                float fullAmount = (shelfStart - viewStart) / fullHeight;
                 float interpolatedAmount =  Interpolators.ACCELERATE_DECELERATE.getInterpolation(
-                        linearAmount);
+                        fullAmount);
                 interpolatedAmount = NotificationUtils.interpolate(
-                        interpolatedAmount, linearAmount, expandAmount);
-                iconAppearAmount = 1.0f - interpolatedAmount;
+                        interpolatedAmount, fullAmount, expandAmount);
+                fullTransitionAmount = 1.0f - interpolatedAmount;
+
+                iconTransitionAmount = (shelfStart - viewStart) / iconTransformDistance;
+                iconTransitionAmount = Math.min(1.0f, iconTransitionAmount);
+                iconTransitionAmount = 1.0f - iconTransitionAmount;
+
             } else {
-                iconAppearAmount = 1.0f;
+                fullTransitionAmount = 1.0f;
+                iconTransitionAmount = 1.0f;
             }
         } else {
-            iconAppearAmount = 0.0f;
+            fullTransitionAmount = 0.0f;
+            iconTransitionAmount = 0.0f;
         }
+        updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount, scrollingFast,
+                expandingAnimated, isLastChild);
+        return fullTransitionAmount;
+    }
 
-        // Lets now calculate how much of the transformation has already happened. This is different
-        // from the above, since we only start transforming when the view is already quite a bit
-        // pushed in.
+    private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount,
+            float fullTransitionAmount, boolean scrollingFast, boolean expandingAnimated,
+            boolean isLastChild) {
+        StatusBarIconView icon = row.getEntry().expandedIcon;
+        NotificationIconContainer.IconState iconState = getIconState(icon);
+        if (iconState == null) {
+            return;
+        }
+        float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;
+        if (clampedAmount == iconTransitionAmount) {
+            iconState.keepClampedPosition = false;
+        }
+        if (clampedAmount == fullTransitionAmount) {
+            iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f || scrollingFast
+                    || expandingAnimated;
+            iconState.translateContent = mMaxLayoutHeight - getTranslationY()
+                    - getIntrinsicHeight() > 0;
+        }
+        if (scrollingFast || (expandingAnimated && iconState.useFullTransitionAmount
+                && !ViewState.isAnimatingY(icon))) {
+            iconState.cancelAnimations(icon);
+            iconState.useFullTransitionAmount = true;
+        }
+        float transitionAmount;
+        boolean needCannedAnimation = iconState.clampedAppearAmount == 1.0f
+                && clampedAmount == 0.0f;
+        if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount) {
+            transitionAmount = iconTransitionAmount;
+        } else if (iconState.keepClampedPosition
+                && iconState.clampedAppearAmount != clampedAmount) {
+            // We animated to the clamped amount but then decided to go the other way. Let's
+            // animate it to the new position
+            transitionAmount = iconTransitionAmount;
+            iconState.needsCannedAnimation = true;
+            iconState.keepClampedPosition = false;
+        } else if (needCannedAnimation || iconState.keepClampedPosition
+                || iconState.iconAppearAmount == 1.0f) {
+            // We need to perform a canned animation since we crossed the treshhold
+            transitionAmount = clampedAmount;
+            iconState.keepClampedPosition = iconState.keepClampedPosition || needCannedAnimation;
+            iconState.needsCannedAnimation = needCannedAnimation;
+        } else {
+            transitionAmount = iconTransitionAmount;
+        }
+        iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
+                    || iconState.useFullTransitionAmount
+                ? fullTransitionAmount
+                : transitionAmount;
+        iconState.clampedAppearAmount = clampedAmount;
+        setIconTransformationAmount(row, transitionAmount);
+        float contentTransformationAmount = isLastChild || iconState.translateContent
+                ? iconTransitionAmount
+                : 0.0f;
+        row.setContentTransformationAmount(contentTransformationAmount, isLastChild);
+    }
+
+    private boolean isLastChild(ExpandableNotificationRow row) {
+        return row == mAmbientState.getLastVisibleBackgroundChild();
+    }
+
+    private void setIconTransformationAmount(ExpandableNotificationRow row,
+            float transitionAmount) {
+        StatusBarIconView icon = row.getEntry().expandedIcon;
+        NotificationIconContainer.IconState iconState = getIconState(icon);
+
         View rowIcon = row.getNotificationIcon();
-        float notificationIconPosition = viewStart;
+        float notificationIconPosition = row.getTranslationY();
         float notificationIconSize = 0.0f;
         int iconTopPadding;
         if (rowIcon != null) {
@@ -322,28 +404,18 @@
         float shelfIconPosition = getTranslationY() + icon.getTop();
         shelfIconPosition += ((1.0f - icon.getIconScale()) * icon.getHeight()) / 2.0f;
         float transitionDistance = getIntrinsicHeight() * 1.5f;
-        if (isLastChild) {
+        if (row == mAmbientState.getLastVisibleBackgroundChild()) {
             transitionDistance = Math.min(transitionDistance, row.getMinHeight()
                     - getIntrinsicHeight());
         }
         float transformationStartPosition = getTranslationY() - transitionDistance;
-        float transitionAmount = 0.0f;
-        if (viewStart < transformationStartPosition
-                || (!mAmbientState.isShadeExpanded()
-                        && (row.isPinned() || row.isHeadsUpAnimatingAway()))) {
-            // We simply place it on the icon of the notification
-            yTranslation = notificationIconPosition - shelfIconPosition;
-        } else {
-            transitionAmount = (viewStart - transformationStartPosition)
-                    / transitionDistance;
-            float startPosition = transformationStartPosition + iconTopPadding;
-            yTranslation = NotificationUtils.interpolate(
-                    startPosition - shelfIconPosition, 0, transitionAmount);
-            // If we are merging into the shelf, lets make sure the shelf is at least on our height,
-            // otherwise the icons won't be visible.
-            setTranslationZ(Math.max(getTranslationZ(), row.getTranslationZ()));
-        }
+        float iconYTranslation = NotificationUtils.interpolate(
+                Math.min(notificationIconPosition, transformationStartPosition + iconTopPadding)
+                        - shelfIconPosition,
+                0,
+                transitionAmount);
         float shelfIconSize = icon.getHeight() * icon.getIconScale();
+        float alpha = 1.0f;
         if (!row.isShowingIcon()) {
             // The view currently doesn't have an icon, lets transform it in!
             alpha = transitionAmount;
@@ -352,15 +424,12 @@
         // The notification size is different from the size in the shelf / statusbar
         float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
                 transitionAmount);
-        row.setIconTransformationAmount(transitionAmount, isLastChild);
         if (iconState != null) {
             iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
             iconState.scaleY = iconState.scaleX;
             iconState.hidden = transitionAmount == 0.0f;
-            iconState.iconAppearAmount = iconAppearAmount;
             iconState.alpha = alpha;
-            iconState.yTranslation = yTranslation;
-            icon.setVisibility(transitionAmount == 0.0f ? INVISIBLE : VISIBLE);
+            iconState.yTranslation = iconYTranslation;
             if (row.isInShelf() && !row.isTransformingIntoShelf()) {
                 iconState.iconAppearAmount = 1.0f;
                 iconState.alpha = 1.0f;
@@ -368,8 +437,14 @@
                 iconState.scaleY = 1.0f;
                 iconState.hidden = false;
             }
+            if (row.isAboveShelf()) {
+                iconState.hidden = true;
+            }
         }
-        return iconAppearAmount;
+    }
+
+    private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) {
+        return mShelfIcons.getIconState(icon);
     }
 
     private float getFullyClosedTranslation() {
@@ -386,9 +461,11 @@
     }
 
     private void setHideBackground(boolean hideBackground) {
-        mHideBackground = hideBackground;
-        updateBackground();
-        updateOutline();
+        if (mHideBackground != hideBackground) {
+            mHideBackground = hideBackground;
+            updateBackground();
+            updateOutline();
+        }
     }
 
     public boolean hidesBackground() {
@@ -415,13 +492,23 @@
                 mShelfIcons.getWidth(),
                 openedAmount);
         mShelfIcons.setActualLayoutWidth(width);
-        float padding = NotificationUtils.interpolate(mCollapsedIcons.getPaddingEnd(),
+        boolean hasOverflow = mCollapsedIcons.hasOverflow();
+        int collapsedPadding = mCollapsedIcons.getPaddingEnd();
+        if (!hasOverflow) {
+            // we have to ensure that adding the low priority notification won't lead to an
+            // overflow
+            collapsedPadding -= (1.0f + NotificationIconContainer.OVERFLOW_EARLY_AMOUNT)
+                    * mCollapsedIcons.getIconSize();
+        }
+        float padding = NotificationUtils.interpolate(collapsedPadding,
                 mShelfIcons.getPaddingEnd(),
                 openedAmount);
         mShelfIcons.setActualPaddingEnd(padding);
         float paddingStart = NotificationUtils.interpolate(start,
                 mShelfIcons.getPaddingStart(), openedAmount);
         mShelfIcons.setActualPaddingStart(paddingStart);
+        mShelfIcons.setOpenedAmount(openedAmount);
+        mShelfIcons.setVisualOverflowAdaption(mCollapsedIcons.getVisualOverflowAdaption());
     }
 
     public void setMaxLayoutHeight(int maxLayoutHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index bfa43fd..dba7130 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -42,7 +42,6 @@
     private float mViewAlpha = 1.0f;
     private ValueAnimator mAlphaAnimator;
     private Rect mExcludedRect = new Rect();
-    private int mLeftInset = 0;
     private boolean mHasExcludedArea;
     private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
             = new ValueAnimator.AnimatorUpdateListener() {
@@ -88,12 +87,12 @@
                 if (mExcludedRect.top > 0) {
                     canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint);
                 }
-                if (mExcludedRect.left + mLeftInset > 0) {
-                    canvas.drawRect(0,  mExcludedRect.top, mExcludedRect.left + mLeftInset,
-                            mExcludedRect.bottom, mPaint);
+                if (mExcludedRect.left > 0) {
+                    canvas.drawRect(0,  mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom,
+                            mPaint);
                 }
-                if (mExcludedRect.right + mLeftInset < getWidth()) {
-                    canvas.drawRect(mExcludedRect.right + mLeftInset,
+                if (mExcludedRect.right < getWidth()) {
+                    canvas.drawRect(mExcludedRect.right,
                             mExcludedRect.top,
                             getWidth(),
                             mExcludedRect.bottom,
@@ -184,14 +183,4 @@
     public void setChangeRunnable(Runnable changeRunnable) {
         mChangeRunnable = changeRunnable;
     }
-
-    public void setLeftInset(int leftInset) {
-        if (mLeftInset != leftInset) {
-            mLeftInset = leftInset;
-
-            if (mHasExcludedArea) {
-                invalidate();
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index d635bb0..a2c2fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -37,6 +37,7 @@
 import android.util.Log;
 import android.util.Property;
 import android.util.TypedValue;
+import android.view.View;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
@@ -102,6 +103,7 @@
     private ObjectAnimator mIconAppearAnimator;
     private ObjectAnimator mDotAnimator;
     private float mDotAppearAmount;
+    private OnVisibilityChangedListener mOnVisibilityChangedListener;
 
     public StatusBarIconView(Context context, String slot, Notification notification) {
         this(context, slot, notification, false);
@@ -453,6 +455,7 @@
     }
 
     public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
+        boolean runnableAdded = false;
         if (visibleState != mVisibleState) {
             mVisibleState = visibleState;
             if (animate) {
@@ -465,20 +468,22 @@
                     targetAmount = 1.0f;
                     interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
                 }
-                mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
-                        targetAmount);
-                mIconAppearAnimator.setInterpolator(interpolator);
-                mIconAppearAnimator.setDuration(100);
-                mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mIconAppearAnimator = null;
-                        if (endRunnable != null) {
-                            endRunnable.run();
+                float currentAmount = getIconAppearAmount();
+                if (targetAmount != currentAmount) {
+                    mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
+                            currentAmount, targetAmount);
+                    mIconAppearAnimator.setInterpolator(interpolator);
+                    mIconAppearAnimator.setDuration(100);
+                    mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mIconAppearAnimator = null;
+                            runRunnable(endRunnable);
                         }
-                    }
-                });
-                mIconAppearAnimator.start();
+                    });
+                    mIconAppearAnimator.start();
+                    runnableAdded = true;
+                }
 
                 if (mDotAnimator != null) {
                     mDotAnimator.cancel();
@@ -489,22 +494,41 @@
                     targetAmount = 1.0f;
                     interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
                 }
-                mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
-                        targetAmount);
-                mDotAnimator.setInterpolator(interpolator);
-                mDotAnimator.setDuration(100);
-                mDotAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mDotAnimator = null;
-                    }
-                });
-                mDotAnimator.start();
+                currentAmount = getDotAppearAmount();
+                if (targetAmount != currentAmount) {
+                    mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
+                            currentAmount, targetAmount);
+                    mDotAnimator.setInterpolator(interpolator);
+                    mDotAnimator.setDuration(100);
+                    final boolean runRunnable = !runnableAdded;
+                    mDotAnimator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mDotAnimator = null;
+                            if (runRunnable) {
+                                runRunnable(endRunnable);
+                            }
+                        }
+                    });
+                    mDotAnimator.start();
+                    runnableAdded = true;
+                }
             } else {
                 setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f);
-                setDotAppearAmount(visibleState == STATE_DOT ? 1.0f : 0.0f);
+                setDotAppearAmount(visibleState == STATE_DOT ? 1.0f
+                        : visibleState == STATE_ICON ? 2.0f
+                        : 0.0f);
             }
         }
+        if (!runnableAdded) {
+            runRunnable(endRunnable);
+        }
+    }
+
+    private void runRunnable(Runnable runnable) {
+        if (runnable != null) {
+            runnable.run();
+        }
     }
 
     public void setIconAppearAmount(float iconAppearAmount) {
@@ -525,7 +549,23 @@
         invalidate();
     }
 
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        if (mOnVisibilityChangedListener != null) {
+            mOnVisibilityChangedListener.onVisibilityChanged(visibility);
+        }
+    }
+
     public float getDotAppearAmount() {
         return mDotAppearAmount;
     }
+
+    public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
+        mOnVisibilityChangedListener = listener;
+    }
+
+    public interface OnVisibilityChangedListener {
+        void onVisibilityChanged(int newVisibility);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index c9f7456..2bc2665 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -18,6 +18,7 @@
 import android.view.View;
 
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
 import java.util.ArrayList;
 
@@ -36,9 +37,9 @@
     private View.OnLongClickListener mLongClickListener;
     private Boolean mLongClickable;
     private Integer mAlpha;
+    private Float mDarkIntensity;
     private Integer mVisibility = -1;
-    private int mImageResource = -1;
-    private Drawable mImageDrawable;
+    private KeyButtonDrawable mImageDrawable;
     private View mCurrentView;
     private boolean mVertical;
 
@@ -61,12 +62,13 @@
         if (mAlpha != null) {
             view.setAlpha(mAlpha);
         }
+        if (mDarkIntensity != null) {
+            ((ButtonInterface) view).setDarkIntensity(mDarkIntensity);
+        }
         if (mVisibility != null) {
             view.setVisibility(mVisibility);
         }
-        if (mImageResource > 0) {
-            ((ButtonInterface) view).setImageResource(mImageResource);
-        } else if (mImageDrawable != null) {
+        if (mImageDrawable != null) {
             ((ButtonInterface) view).setImageDrawable(mImageDrawable);
         }
 
@@ -87,24 +89,14 @@
         return mAlpha != null ? mAlpha : 1;
     }
 
-    public void setImageDrawable(Drawable drawable) {
+    public void setImageDrawable(KeyButtonDrawable drawable) {
         mImageDrawable = drawable;
-        mImageResource = -1;
         final int N = mViews.size();
         for (int i = 0; i < N; i++) {
             ((ButtonInterface) mViews.get(i)).setImageDrawable(mImageDrawable);
         }
     }
 
-    public void setImageResource(int resource) {
-        mImageResource = resource;
-        mImageDrawable = null;
-        final int N = mViews.size();
-        for (int i = 0; i < N; i++) {
-            ((ButtonInterface) mViews.get(i)).setImageResource(mImageResource);
-        }
-    }
-
     public void setVisibility(int visibility) {
         if (mVisibility == visibility) return;
         mVisibility = visibility;
@@ -130,6 +122,14 @@
         }
     }
 
+    public void setDarkIntensity(float darkIntensity) {
+        mDarkIntensity = darkIntensity;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            ((ButtonInterface) mViews.get(i)).setDarkIntensity(darkIntensity);
+        }
+    }
+
     public void setOnClickListener(View.OnClickListener clickListener) {
         mClickListener = clickListener;
         final int N = mViews.size();
@@ -194,5 +194,4 @@
             }
         }
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index daa57c6..a2c106a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -32,6 +32,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -143,6 +144,7 @@
 
     private boolean mLeftIsVoiceAssist;
     private AssistManager mAssistManager;
+    private Drawable mLeftAssistIcon;
 
     private IntentButton mRightButton = new DefaultRightButton();
     private IntentButton mLeftButton = new DefaultLeftButton();
@@ -348,6 +350,14 @@
                 ? View.VISIBLE : View.GONE);
     }
 
+    /**
+     * Set an alternate icon for the left assist affordance (replace the mic icon)
+     */
+    public void setLeftAssistIcon(Drawable drawable) {
+        mLeftAssistIcon = drawable;
+        updateLeftAffordanceIcon();
+    }
+
     private void updateLeftAffordanceIcon() {
         IconState state = mLeftButton.getIcon();
         mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
@@ -814,7 +824,11 @@
             mLeftIsVoiceAssist = canLaunchVoiceAssist();
             if (mLeftIsVoiceAssist) {
                 mIconState.isVisible = mUserSetupComplete;
-                mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
+                if (mLeftAssistIcon == null) {
+                    mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
+                } else {
+                    mIconState.drawable = mLeftAssistIcon;
+                }
                 mIconState.contentDescription = mContext.getString(
                         R.string.accessibility_voice_assist_button);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
new file mode 100644
index 0000000..b5358a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+/**
+ * Controls how light status bar flag applies to the icons.
+ */
+public class LightBarController implements BatteryController.BatteryStateChangeCallback {
+
+    private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
+
+    private final StatusBarIconController mStatusBarIconController;
+    private final BatteryController mBatteryController;
+    private FingerprintUnlockController mFingerprintUnlockController;
+    private final NavigationBarView mNavigationBarView;
+
+    private int mSystemUiVisibility;
+    private int mFullscreenStackVisibility;
+    private int mDockedStackVisibility;
+    private boolean mFullscreenLight;
+    private boolean mDockedLight;
+    private int mLastStatusBarMode;
+    private int mLastNavigationBarMode;
+    private boolean mNavigationLight;
+    private float mScrimAlpha;
+
+    private final Rect mLastFullscreenBounds = new Rect();
+    private final Rect mLastDockedBounds = new Rect();
+
+    public LightBarController(StatusBarIconController statusBarIconController,
+            NavigationBarView navigationBarView,
+            BatteryController batteryController) {
+        mStatusBarIconController = statusBarIconController;
+        mNavigationBarView = navigationBarView;
+        mBatteryController = batteryController;
+        batteryController.addCallback(this);
+    }
+
+    public void setFingerprintUnlockController(
+            FingerprintUnlockController fingerprintUnlockController) {
+        mFingerprintUnlockController = fingerprintUnlockController;
+    }
+
+    public void onSystemUiVisibilityChanged(int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+            int statusBarMode, boolean nbModeChanged, int navigationBarMode) {
+        int oldFullscreen = mFullscreenStackVisibility;
+        int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
+        int diffFullscreen = newFullscreen ^ oldFullscreen;
+        int oldDocked = mDockedStackVisibility;
+        int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
+        int diffDocked = newDocked ^ oldDocked;
+        if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                || sbModeChanged
+                || !mLastFullscreenBounds.equals(fullscreenStackBounds)
+                || !mLastDockedBounds.equals(dockedStackBounds)) {
+
+            mFullscreenLight = isLight(newFullscreen, statusBarMode,
+                    View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+            mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+            updateStatus(fullscreenStackBounds, dockedStackBounds);
+        }
+
+        int oldVis = mSystemUiVisibility;
+        int newVis = (oldVis & ~mask) | (vis & mask);
+        int diffVis = newVis ^ oldVis;
+        if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
+                || nbModeChanged) {
+            boolean last = mNavigationLight;
+            mNavigationLight = isNavigationLight(newVis, navigationBarMode);
+            if (mNavigationLight != last) {
+                updateNavigation();
+            }
+        }
+        mFullscreenStackVisibility = newFullscreen;
+        mDockedStackVisibility = newDocked;
+        mSystemUiVisibility = newVis;
+        mLastStatusBarMode = statusBarMode;
+        mLastNavigationBarMode = navigationBarMode;
+        mLastFullscreenBounds.set(fullscreenStackBounds);
+        mLastDockedBounds.set(dockedStackBounds);
+    }
+
+    private void reevaluate() {
+        onSystemUiVisibilityChanged(mSystemUiVisibility, mFullscreenStackVisibility,
+                mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds,
+                true /* sbModeChange*/, mLastStatusBarMode, true /* nbModeChange*/,
+                mLastNavigationBarMode);
+    }
+
+    public void setScrimAlpha(float alpha) {
+        mScrimAlpha = alpha;
+        reevaluate();
+    }
+
+    private boolean isNavigationLight(int vis, int barMode) {
+        return isLight(vis, barMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
+                && mScrimAlpha < NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD;
+    }
+
+    private boolean isLight(int vis, int barMode, int flag) {
+        boolean isTransparentBar = (barMode == MODE_TRANSPARENT
+                || barMode == MODE_LIGHTS_OUT_TRANSPARENT);
+        boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+        boolean light = (vis & flag) != 0;
+        return allowLight && light;
+    }
+
+    private boolean animateChange() {
+        if (mFingerprintUnlockController == null) {
+            return false;
+        }
+        int unlockMode = mFingerprintUnlockController.getMode();
+        return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+    }
+
+    private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        boolean hasDockedStack = !dockedStackBounds.isEmpty();
+
+        // If both are light or fullscreen is light and there is no docked stack, all icons get
+        // dark.
+        if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+            mStatusBarIconController.setIconsDarkArea(null);
+            mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
+
+        }
+
+        // If no one is light or the fullscreen is not light and there is no docked stack,
+        // all icons become white.
+        else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+            mStatusBarIconController.getTransitionsController().setIconsDark(
+                    false, animateChange());
+        }
+
+        // Not the same for every stack, magic!
+        else {
+            Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
+            if (bounds.isEmpty()) {
+                mStatusBarIconController.setIconsDarkArea(null);
+            } else {
+                mStatusBarIconController.setIconsDarkArea(bounds);
+            }
+            mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
+        }
+    }
+
+    private void updateNavigation() {
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getLightTransitionsController().setIconsDark(
+                    mNavigationLight, animateChange());
+        }
+    }
+
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+
+    }
+
+    @Override
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        reevaluate();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
new file mode 100644
index 0000000..1d4d2d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.ValueAnimator;
+import android.os.Handler;
+import android.os.SystemClock;
+
+import com.android.systemui.Interpolators;
+
+/**
+ * Class to control all aspects about light bar changes.
+ */
+public class LightBarTransitionsController {
+
+    public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
+
+    private final Handler mHandler;
+    private final DarkIntensityApplier mApplier;
+
+    private boolean mTransitionDeferring;
+    private long mTransitionDeferringStartTime;
+    private long mTransitionDeferringDuration;
+    private boolean mTransitionPending;
+    private boolean mTintChangePending;
+    private float mPendingDarkIntensity;
+    private ValueAnimator mTintAnimator;
+    private float mDarkIntensity;
+
+    private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mTransitionDeferring = false;
+        }
+    };
+
+    public LightBarTransitionsController(DarkIntensityApplier applier) {
+        mApplier = applier;
+        mHandler = new Handler();
+    }
+
+    public void appTransitionPending() {
+        mTransitionPending = true;
+    }
+
+    public void appTransitionCancelled() {
+        if (mTransitionPending && mTintChangePending) {
+            mTintChangePending = false;
+            animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+        }
+        mTransitionPending = false;
+    }
+
+    public void appTransitionStarting(long startTime, long duration) {
+        if (mTransitionPending && mTintChangePending) {
+            mTintChangePending = false;
+            animateIconTint(mPendingDarkIntensity,
+                    Math.max(0, startTime - SystemClock.uptimeMillis()),
+                    duration);
+
+        } else if (mTransitionPending) {
+
+            // If we don't have a pending tint change yet, the change might come in the future until
+            // startTime is reached.
+            mTransitionDeferring = true;
+            mTransitionDeferringStartTime = startTime;
+            mTransitionDeferringDuration = duration;
+            mHandler.removeCallbacks(mTransitionDeferringDoneRunnable);
+            mHandler.postAtTime(mTransitionDeferringDoneRunnable, startTime);
+        }
+        mTransitionPending = false;
+    }
+
+    public void setIconsDark(boolean dark, boolean animate) {
+        if (!animate) {
+            setIconTintInternal(dark ? 1.0f : 0.0f);
+        } else if (mTransitionPending) {
+            deferIconTintChange(dark ? 1.0f : 0.0f);
+        } else if (mTransitionDeferring) {
+            animateIconTint(dark ? 1.0f : 0.0f,
+                    Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
+                    mTransitionDeferringDuration);
+        } else {
+            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+        }
+    }
+
+    public float getCurrentDarkIntensity() {
+        return mDarkIntensity;
+    }
+
+    private void deferIconTintChange(float darkIntensity) {
+        if (mTintChangePending && darkIntensity == mPendingDarkIntensity) {
+            return;
+        }
+        mTintChangePending = true;
+        mPendingDarkIntensity = darkIntensity;
+    }
+
+    private void animateIconTint(float targetDarkIntensity, long delay,
+            long duration) {
+        if (mTintAnimator != null) {
+            mTintAnimator.cancel();
+        }
+        if (mDarkIntensity == targetDarkIntensity) {
+            return;
+        }
+        mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity);
+        mTintAnimator.addUpdateListener(
+                animation -> setIconTintInternal((Float) animation.getAnimatedValue()));
+        mTintAnimator.setDuration(duration);
+        mTintAnimator.setStartDelay(delay);
+        mTintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mTintAnimator.start();
+    }
+
+    private void setIconTintInternal(float darkIntensity) {
+        mDarkIntensity = darkIntensity;
+        mApplier.applyDarkIntensity(darkIntensity);
+    }
+
+    /**
+     * Interface to apply a specific dark intensity.
+     */
+    public interface DarkIntensityApplier {
+        void applyDarkIntensity(float darkIntensity);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
deleted file mode 100644
index dd7f3cc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.graphics.Rect;
-import android.view.View;
-
-import com.android.systemui.statusbar.policy.BatteryController;
-
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-
-/**
- * Controls how light status bar flag applies to the icons.
- */
-public class LightStatusBarController implements BatteryController.BatteryStateChangeCallback {
-
-    private final StatusBarIconController mIconController;
-    private final BatteryController mBatteryController;
-    private FingerprintUnlockController mFingerprintUnlockController;
-
-    private int mFullscreenStackVisibility;
-    private int mDockedStackVisibility;
-    private boolean mFullscreenLight;
-    private boolean mDockedLight;
-    private int mLastStatusBarMode;
-
-    private final Rect mLastFullscreenBounds = new Rect();
-    private final Rect mLastDockedBounds = new Rect();
-
-    public LightStatusBarController(StatusBarIconController iconController,
-            BatteryController batteryController) {
-        mIconController = iconController;
-        mBatteryController = batteryController;
-        batteryController.addCallback(this);
-    }
-
-    public void setFingerprintUnlockController(
-            FingerprintUnlockController fingerprintUnlockController) {
-        mFingerprintUnlockController = fingerprintUnlockController;
-    }
-
-    public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask,
-            Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
-            int statusBarMode) {
-        int oldFullscreen = mFullscreenStackVisibility;
-        int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
-        int diffFullscreen = newFullscreen ^ oldFullscreen;
-        int oldDocked = mDockedStackVisibility;
-        int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
-        int diffDocked = newDocked ^ oldDocked;
-        if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
-                || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
-                || sbModeChanged
-                || !mLastFullscreenBounds.equals(fullscreenStackBounds)
-                || !mLastDockedBounds.equals(dockedStackBounds)) {
-
-            mFullscreenLight = isLight(newFullscreen, statusBarMode);
-            mDockedLight = isLight(newDocked, statusBarMode);
-            update(fullscreenStackBounds, dockedStackBounds);
-        }
-        mFullscreenStackVisibility = newFullscreen;
-        mDockedStackVisibility = newDocked;
-        mLastStatusBarMode = statusBarMode;
-        mLastFullscreenBounds.set(fullscreenStackBounds);
-        mLastDockedBounds.set(dockedStackBounds);
-    }
-
-    private boolean isLight(int vis, int statusBarMode) {
-        boolean isTransparentBar = (statusBarMode == MODE_TRANSPARENT
-                || statusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
-        boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
-        boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
-        return allowLight && light;
-    }
-
-    private boolean animateChange() {
-        if (mFingerprintUnlockController == null) {
-            return false;
-        }
-        int unlockMode = mFingerprintUnlockController.getMode();
-        return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
-    }
-
-    private void update(Rect fullscreenStackBounds, Rect dockedStackBounds) {
-        boolean hasDockedStack = !dockedStackBounds.isEmpty();
-
-        // If both are light or fullscreen is light and there is no docked stack, all icons get
-        // dark.
-        if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
-            mIconController.setIconsDarkArea(null);
-            mIconController.setIconsDark(true, animateChange());
-
-        }
-
-        // If no one is light or the fullscreen is not light and there is no docked stack,
-        // all icons become white.
-        else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
-            mIconController.setIconsDark(false, animateChange());
-
-        }
-
-        // Not the same for every stack, magic!
-        else {
-            Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
-            if (bounds.isEmpty()) {
-                mIconController.setIconsDarkArea(null);
-            } else {
-                mIconController.setIconsDarkArea(bounds);
-            }
-            mIconController.setIconsDark(true, animateChange());
-        }
-    }
-
-    @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-
-    }
-
-    @Override
-    public void onPowerSaveChanged(boolean isPowerSave) {
-        onSystemUiVisibilityChanged(mFullscreenStackVisibility, mDockedStackVisibility,
-                0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, true /* sbModeChange*/,
-                mLastStatusBarMode);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
index 1a46815..4969a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -14,134 +14,16 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.app.ActivityManager;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-
 import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
 import com.android.systemui.statusbar.policy.CallbackController;
 
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
+public interface ManagedProfileController extends CallbackController<Callback> {
 
-public class ManagedProfileController implements CallbackController<Callback> {
+    void setWorkModeEnabled(boolean enabled);
 
-    private final List<Callback> mCallbacks = new ArrayList<>();
+    boolean hasActiveProfile();
 
-    private final Context mContext;
-    private final UserManager mUserManager;
-    private final LinkedList<UserInfo> mProfiles;
-    private boolean mListening;
-    private int mCurrentUser;
-
-    public ManagedProfileController(QSTileHost host) {
-        mContext = host.getContext();
-        mUserManager = UserManager.get(mContext);
-        mProfiles = new LinkedList<UserInfo>();
-    }
-
-    public void addCallback(Callback callback) {
-        mCallbacks.add(callback);
-        if (mCallbacks.size() == 1) {
-            setListening(true);
-        }
-        callback.onManagedProfileChanged();
-    }
-
-    public void removeCallback(Callback callback) {
-        if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
-            setListening(false);
-        }
-    }
-
-    public void setWorkModeEnabled(boolean enableWorkMode) {
-        synchronized (mProfiles) {
-            for (UserInfo ui : mProfiles) {
-                if (enableWorkMode) {
-                    if (!mUserManager.trySetQuietModeDisabled(ui.id, null)) {
-                        StatusBarManager statusBarManager = (StatusBarManager) mContext
-                                .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
-                        statusBarManager.collapsePanels();
-                    }
-                } else {
-                    mUserManager.setQuietModeEnabled(ui.id, true);
-                }
-            }
-        }
-    }
-
-    private void reloadManagedProfiles() {
-        synchronized (mProfiles) {
-            boolean hadProfile = mProfiles.size() > 0;
-            int user = ActivityManager.getCurrentUser();
-            mProfiles.clear();
-
-            for (UserInfo ui : mUserManager.getEnabledProfiles(user)) {
-                if (ui.isManagedProfile()) {
-                    mProfiles.add(ui);
-                }
-            }
-            if (mProfiles.size() == 0 && hadProfile && (user == mCurrentUser)) {
-                for (Callback callback : mCallbacks) {
-                    callback.onManagedProfileRemoved();
-                }
-            }
-            mCurrentUser = user;
-        }
-    }
-
-    public boolean hasActiveProfile() {
-        if (!mListening) reloadManagedProfiles();
-        synchronized (mProfiles) {
-            return mProfiles.size() > 0;
-        }
-    }
-
-    public boolean isWorkModeEnabled() {
-        if (!mListening) reloadManagedProfiles();
-        synchronized (mProfiles) {
-            for (UserInfo ui : mProfiles) {
-                if (ui.isQuietModeEnabled()) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    private void setListening(boolean listening) {
-        mListening = listening;
-        if (listening) {
-            reloadManagedProfiles();
-
-            final IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
-            filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
-            mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
-        } else {
-            mContext.unregisterReceiver(mReceiver);
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            reloadManagedProfiles();
-            for (Callback callback : mCallbacks) {
-                callback.onManagedProfileChanged();
-            }
-        }
-    };
+    boolean isWorkModeEnabled();
 
     public interface Callback {
         void onManagedProfileChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
new file mode 100644
index 0000000..fc33ace
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.app.ActivityManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ManagedProfileControllerImpl implements ManagedProfileController {
+
+    private final List<Callback> mCallbacks = new ArrayList<>();
+
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private final LinkedList<UserInfo> mProfiles;
+    private boolean mListening;
+    private int mCurrentUser;
+
+    public ManagedProfileControllerImpl(QSTileHost host) {
+        mContext = host.getContext();
+        mUserManager = UserManager.get(mContext);
+        mProfiles = new LinkedList<UserInfo>();
+    }
+
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
+        if (mCallbacks.size() == 1) {
+            setListening(true);
+        }
+        callback.onManagedProfileChanged();
+    }
+
+    public void removeCallback(Callback callback) {
+        if (mCallbacks.remove(callback) && mCallbacks.size() == 0) {
+            setListening(false);
+        }
+    }
+
+    public void setWorkModeEnabled(boolean enableWorkMode) {
+        synchronized (mProfiles) {
+            for (UserInfo ui : mProfiles) {
+                if (enableWorkMode) {
+                    if (!mUserManager.trySetQuietModeDisabled(ui.id, null)) {
+                        StatusBarManager statusBarManager = (StatusBarManager) mContext
+                                .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+                        statusBarManager.collapsePanels();
+                    }
+                } else {
+                    mUserManager.setQuietModeEnabled(ui.id, true);
+                }
+            }
+        }
+    }
+
+    private void reloadManagedProfiles() {
+        synchronized (mProfiles) {
+            boolean hadProfile = mProfiles.size() > 0;
+            int user = ActivityManager.getCurrentUser();
+            mProfiles.clear();
+
+            for (UserInfo ui : mUserManager.getEnabledProfiles(user)) {
+                if (ui.isManagedProfile()) {
+                    mProfiles.add(ui);
+                }
+            }
+            if (mProfiles.size() == 0 && hadProfile && (user == mCurrentUser)) {
+                for (Callback callback : mCallbacks) {
+                    callback.onManagedProfileRemoved();
+                }
+            }
+            mCurrentUser = user;
+        }
+    }
+
+    public boolean hasActiveProfile() {
+        if (!mListening) reloadManagedProfiles();
+        synchronized (mProfiles) {
+            return mProfiles.size() > 0;
+        }
+    }
+
+    public boolean isWorkModeEnabled() {
+        if (!mListening) reloadManagedProfiles();
+        synchronized (mProfiles) {
+            for (UserInfo ui : mProfiles) {
+                if (ui.isQuietModeEnabled()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private void setListening(boolean listening) {
+        mListening = listening;
+        if (listening) {
+            reloadManagedProfiles();
+
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_USER_SWITCHED);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+            filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+            mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
+        } else {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            reloadManagedProfiles();
+            for (Callback callback : mCallbacks) {
+                callback.onManagedProfileChanged();
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 1fe0115..3be5e57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -16,13 +16,11 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.os.ServiceManager;
+import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.animation.AccelerateInterpolator;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.R;
@@ -31,6 +29,7 @@
 
     private final NavigationBarView mView;
     private final IStatusBarService mBarService;
+    private final LightBarTransitionsController mLightTransitionsController;
 
     private boolean mLightsOut;
 
@@ -39,6 +38,7 @@
         mView = view;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mLightTransitionsController = new LightBarTransitionsController(this::applyDarkIntensity);
     }
 
     public void init() {
@@ -46,6 +46,10 @@
         applyMode(getMode(), false /*animate*/, true /*force*/);
     }
 
+    public LightBarTransitionsController getLightTransitionsController() {
+        return mLightTransitionsController;
+    }
+
     @Override
     protected void onTransition(int oldMode, int newMode, boolean animate) {
         super.onTransition(oldMode, newMode, animate);
@@ -81,6 +85,18 @@
         }
     }
 
+
+    public void reapplyDarkIntensity() {
+        applyDarkIntensity(mLightTransitionsController.getCurrentDarkIntensity());
+    }
+
+    public void applyDarkIntensity(float darkIntensity) {
+        SparseArray<ButtonDispatcher> buttonDispatchers = mView.getButtonDispatchers();
+        for (int i = buttonDispatchers.size() - 1; i >= 0; i--) {
+            buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity);
+        }
+    }
+
     private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7023324..d22f421 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -22,6 +22,7 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
+import android.annotation.DrawableRes;
 import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -34,6 +35,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.IDockedStackListener.Stub;
 import android.view.MotionEvent;
@@ -51,8 +53,10 @@
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.phone.NavGesture;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.policy.DeadZone;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -78,14 +82,14 @@
     int mDisabledFlags = 0;
     int mNavigationIconHints = 0;
 
-    private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
-    private Drawable mBackCarModeIcon, mBackLandCarModeIcon;
-    private Drawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
-    private Drawable mHomeDefaultIcon, mHomeCarModeIcon;
-    private Drawable mRecentIcon;
-    private Drawable mDockedIcon;
-    private Drawable mImeIcon;
-    private Drawable mMenuIcon;
+    private KeyButtonDrawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
+    private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon;
+    private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
+    private KeyButtonDrawable mHomeDefaultIcon, mHomeCarModeIcon;
+    private KeyButtonDrawable mRecentIcon;
+    private KeyButtonDrawable mDockedIcon;
+    private KeyButtonDrawable mImeIcon;
+    private KeyButtonDrawable mMenuIcon;
 
     private GestureHelper mGestureHelper;
     private DeadZone mDeadZone;
@@ -217,6 +221,10 @@
         return mBarTransitions;
     }
 
+    public LightBarTransitionsController getLightTransitionsController() {
+        return mBarTransitions.getLightTransitionsController();
+    }
+
     public void setComponents(RecentsComponent recentsComponent, Divider divider) {
         mRecentsComponent = recentsComponent;
         mDivider = divider;
@@ -281,29 +289,44 @@
         return mButtonDispatchers.get(R.id.ime_switcher);
     }
 
+    public SparseArray<ButtonDispatcher> getButtonDispatchers() {
+        return mButtonDispatchers;
+    }
+
     private void updateCarModeIcons(Context ctx) {
-        mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode);
+        mBackCarModeIcon = getDrawable(ctx,
+                R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
         mBackLandCarModeIcon = mBackCarModeIcon;
-        mBackAltCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime_carmode);
+        mBackAltCarModeIcon = getDrawable(ctx,
+                R.drawable.ic_sysbar_back_ime_carmode, R.drawable.ic_sysbar_back_ime_carmode);
         mBackAltLandCarModeIcon = mBackAltCarModeIcon;
-        mHomeCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_home_carmode);
+        mHomeCarModeIcon = getDrawable(ctx,
+                R.drawable.ic_sysbar_home_carmode, R.drawable.ic_sysbar_home_carmode);
     }
 
     private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
         if (oldConfig.orientation != newConfig.orientation
                 || oldConfig.densityDpi != newConfig.densityDpi) {
-            mDockedIcon = ctx.getDrawable(R.drawable.ic_sysbar_docked);
+            mDockedIcon = getDrawable(ctx,
+                    R.drawable.ic_sysbar_docked, R.drawable.ic_sysbar_docked_dark);
         }
         if (oldConfig.densityDpi != newConfig.densityDpi) {
-            mBackIcon = ctx.getDrawable(R.drawable.ic_sysbar_back);
+            mBackIcon = getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
             mBackLandIcon = mBackIcon;
-            mBackAltIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime);
+            mBackAltIcon = getDrawable(ctx,
+                    R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark);
             mBackAltLandIcon = mBackAltIcon;
 
-            mHomeDefaultIcon = ctx.getDrawable(R.drawable.ic_sysbar_home);
-            mRecentIcon = ctx.getDrawable(R.drawable.ic_sysbar_recent);
-            mMenuIcon = ctx.getDrawable(R.drawable.ic_sysbar_menu);
-            mImeIcon = ctx.getDrawable(R.drawable.ic_ime_switcher_default);
+            mHomeDefaultIcon = getDrawable(ctx,
+                    R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
+            mRecentIcon = getDrawable(ctx,
+                    R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark);
+            mMenuIcon = getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark);
+
+            Context darkContext = new ContextThemeWrapper(ctx, R.style.DualToneDarkTheme);
+            Context lightContext = new ContextThemeWrapper(ctx, R.style.DualToneLightTheme);
+            mImeIcon = getDrawable(darkContext, lightContext,
+                    R.drawable.ic_ime_switcher_default, R.drawable.ic_ime_switcher_default);
 
             if (ALTERNATE_CAR_MODE_UI) {
                 updateCarModeIcons(ctx);
@@ -311,6 +334,17 @@
         }
     }
 
+    private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int lightIcon,
+            @DrawableRes int darkIcon) {
+        return getDrawable(ctx, ctx, lightIcon, darkIcon);
+    }
+
+    private KeyButtonDrawable getDrawable(Context darkContext, Context lightContext,
+            @DrawableRes int lightIcon, @DrawableRes int darkIcon) {
+        return KeyButtonDrawable.create(lightContext.getDrawable(lightIcon),
+                darkContext.getDrawable(darkIcon));
+    }
+
     @Override
     public void setLayoutDirection(int layoutDirection) {
         // Reload all the icons
@@ -328,13 +362,13 @@
         setNavigationIconHints(hints, false);
     }
 
-    private Drawable getBackIconWithAlt(boolean carMode, boolean landscape) {
+    private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) {
         return landscape
                 ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon
                 : carMode ? mBackAltCarModeIcon : mBackAltIcon;
     }
 
-    private Drawable getBackIcon(boolean carMode, boolean landscape) {
+    private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) {
         return landscape
                 ? carMode ? mBackLandCarModeIcon : mBackLandIcon
                 : carMode ? mBackCarModeIcon : mBackIcon;
@@ -357,7 +391,7 @@
         // We have to replace or restore the back and home button icons when exiting or entering
         // carmode, respectively. Recents are not available in CarMode in nav bar so change
         // to recent icon is not required.
-        Drawable backIcon = (backAlt)
+        KeyButtonDrawable backIcon = (backAlt)
                 ? getBackIconWithAlt(mUseCarModeUi, mVertical)
                 : getBackIcon(mUseCarModeUi, mVertical);
 
@@ -380,6 +414,8 @@
         getMenuButton().setImageDrawable(mMenuIcon);
 
         setDisabledFlags(mDisabledFlags, true);
+
+        mBarTransitions.reapplyDarkIntensity();
     }
 
     public void setDisabledFlags(int disabledFlags) {
@@ -565,6 +601,7 @@
 
     private void updateRecentsIcon() {
         getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
+        mBarTransitions.reapplyDarkIntensity();
     }
 
     public boolean isVertical() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index d543f49..345dcbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -127,9 +127,9 @@
         return mPhoneStatusBar.getStatusBarHeight();
     }
 
-    protected boolean shouldShowNotification(NotificationData.Entry entry,
-            NotificationData notificationData) {
-        if (notificationData.isAmbient(entry.key)
+    protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
+            NotificationData notificationData, boolean showAmbient) {
+        if (notificationData.isAmbient(entry.key) && !showAmbient
                 && !NotificationData.showNotificationEvenIfUnprovisioned(entry.notification)) {
             return false;
         }
@@ -148,8 +148,10 @@
      */
     public void updateNotificationIcons(NotificationData notificationData) {
 
-        updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons);
-        updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons);
+        updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
+                false /* showAmbient */);
+        updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
+                NotificationShelf.SHOW_AMBIENT_ICONS);
 
         applyNotificationIconsTint();
         ArrayList<NotificationData.Entry> activeNotifications
@@ -173,10 +175,11 @@
      * @param notificationData the notification data to look up which notifications are relevant
      * @param function A function to look up an icon view based on an entry
      * @param hostLayout which layout should be updated
+     * @param showAmbient should ambient notification icons be shown
      */
     private void updateIconsForLayout(NotificationData notificationData,
             Function<NotificationData.Entry, StatusBarIconView> function,
-            NotificationIconContainer hostLayout) {
+            NotificationIconContainer hostLayout, boolean showAmbient) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -185,7 +188,7 @@
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
                 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
-                if (shouldShowNotification(ent, notificationData)) {
+                if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
                     toShow.add(function.apply(ent));
                 }
             }
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 03697b8..d323e4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -31,15 +31,23 @@
 import com.android.systemui.statusbar.stack.AnimationProperties;
 import com.android.systemui.statusbar.stack.ViewState;
 
-import java.util.WeakHashMap;
+import java.util.HashMap;
 
 /**
  * A container for notification icons. It handles overflowing icons properly and positions them
  * correctly on the screen.
  */
 public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
+    /**
+     * A float value indicating how much before the overflow start the icons should transform into
+     * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts
+     * 1 icon width early.
+     */
+    public static final float OVERFLOW_EARLY_AMOUNT = 0.2f;
+    private static final int NO_VALUE = Integer.MIN_VALUE;
     private static final String TAG = "NotificationIconContainer";
     private static final boolean DEBUG = false;
+    private static final int CANNED_ANIMATION_DURATION = 100;
     private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
         private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
 
@@ -49,6 +57,26 @@
         }
     }.setDuration(200);
 
+    private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha();
+        // TODO: add scale
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(CANNED_ANIMATION_DURATION);
+
+    private static final AnimationProperties mTempProperties = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter();
+        // TODO: add scale
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(CANNED_ANIMATION_DURATION);
+
     private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
         private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
 
@@ -59,14 +87,19 @@
     }.setDuration(200).setDelay(50);
 
     private boolean mShowAllIcons = true;
-    private WeakHashMap<View, IconState> mIconStates = new WeakHashMap<>();
+    private final HashMap<View, IconState> mIconStates = new HashMap<>();
     private int mDotPadding;
     private int mStaticDotRadius;
-    private int mActualLayoutWidth = -1;
-    private float mActualPaddingEnd = -1;
-    private float mActualPaddingStart = -1;
+    private int mActualLayoutWidth = NO_VALUE;
+    private float mActualPaddingEnd = NO_VALUE;
+    private float mActualPaddingStart = NO_VALUE;
     private boolean mChangingViewPositions;
-    private int mAnimationStartIndex = -1;
+    private int mAddAnimationStartIndex = -1;
+    private int mCannedAnimationStartIndex = -1;
+    private int mSpeedBumpIndex = -1;
+    private int mIconSize;
+    private float mOpenedAmount = 0.0f;
+    private float mVisualOverflowAdaption;
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -97,6 +130,7 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         float centerY = getHeight() / 2.0f;
         // we layout all our children on the left at the top
+        mIconSize = 0;
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
             // We need to layout all children even the GONE ones, such that the heights are
@@ -105,6 +139,9 @@
             int height = child.getMeasuredHeight();
             int top = (int) (centerY - height / 2.0f);
             child.layout(0, top, width, top + height);
+            if (i == 0) {
+                mIconSize = child.getWidth();
+            }
         }
         if (mShowAllIcons) {
             resetViewStates();
@@ -121,7 +158,8 @@
                 childState.applyToView(child);
             }
         }
-        mAnimationStartIndex = -1;
+        mAddAnimationStartIndex = -1;
+        mCannedAnimationStartIndex = -1;
     }
 
     @Override
@@ -133,10 +171,10 @@
         int childIndex = indexOfChild(child);
         if (childIndex < getChildCount() - 1
             && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) {
-            if (mAnimationStartIndex < 0) {
-                mAnimationStartIndex = childIndex;
+            if (mAddAnimationStartIndex < 0) {
+                mAddAnimationStartIndex = childIndex;
             } else {
-                mAnimationStartIndex = Math.min(mAnimationStartIndex, childIndex);
+                mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
             }
         }
     }
@@ -149,10 +187,10 @@
             if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                     && child.getVisibility() == VISIBLE) {
                 int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
-                if (mAnimationStartIndex < 0) {
-                    mAnimationStartIndex = animationStartIndex;
+                if (mAddAnimationStartIndex < 0) {
+                    mAddAnimationStartIndex = animationStartIndex;
                 } else {
-                    mAnimationStartIndex = Math.min(mAnimationStartIndex, animationStartIndex);
+                    mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, animationStartIndex);
                 }
             }
             if (!mChangingViewPositions) {
@@ -177,14 +215,13 @@
         return getChildCount();
     }
 
-    public WeakHashMap<View, IconState> resetViewStates() {
+    public void resetViewStates() {
         for (int i = 0; i < getChildCount(); i++) {
             View view = getChildAt(i);
             ViewState iconState = mIconStates.get(view);
             iconState.initFrom(view);
             iconState.alpha = 1.0f;
         }
-        return mIconStates;
     }
 
     /**
@@ -194,42 +231,74 @@
      */
     public void calculateIconTranslations() {
         float translationX = getActualPaddingStart();
-        int overflowingIconIndex = -1;
-        int lastTwoIconWidth = 0;
+        int firstOverflowIndex = -1;
         int childCount = getChildCount();
+        float layoutEnd = getLayoutEnd();
+        float overflowStart = layoutEnd - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT);
+        boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
+        float visualOverflowStart = 0;
         for (int i = 0; i < childCount; i++) {
             View view = getChildAt(i);
             IconState iconState = mIconStates.get(view);
             iconState.xTranslation = translationX;
-            iconState.visibleState = StatusBarIconView.STATE_ICON;
-            translationX += iconState.iconAppearAmount * view.getWidth();
-            if (translationX > getLayoutEnd()) {
-                // we are overflowing it with this icon
-                overflowingIconIndex = i - 1;
-                lastTwoIconWidth = view.getWidth();
-                break;
+            boolean isAmbient = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
+                    && iconState.iconAppearAmount > 0.0f;
+            boolean noOverflowAfter = i == childCount - 1;
+            if (mOpenedAmount != 0.0f) {
+                noOverflowAfter = noOverflowAfter && !hasAmbient;
             }
+            iconState.visibleState = StatusBarIconView.STATE_ICON;
+            if (firstOverflowIndex == -1 && (isAmbient
+                    || (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)))) {
+                firstOverflowIndex = noOverflowAfter ? i - 1 : i;
+                int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding;
+                visualOverflowStart = overflowStart + mIconSize * (1 + OVERFLOW_EARLY_AMOUNT)
+                        - totalDotLength / 2
+                        - mIconSize * 0.5f + mStaticDotRadius;
+                if (isAmbient) {
+                    visualOverflowStart = Math.min(translationX, visualOverflowStart
+                            + mStaticDotRadius * 2 + mDotPadding);
+                } else {
+                    visualOverflowStart += (translationX - overflowStart) / mIconSize
+                            * (mStaticDotRadius * 2 + mDotPadding);
+                }
+                if (mShowAllIcons) {
+                    // We want to perfectly position the overflow in the static state, such that
+                    // it's perfectly centered instead of measuring it from the end.
+                    mVisualOverflowAdaption = 0;
+                    if (firstOverflowIndex != -1) {
+                        View firstOverflowView = getChildAt(i);
+                        IconState overflowState = mIconStates.get(firstOverflowView);
+                        float totalAmount = layoutEnd - overflowState.xTranslation;
+                        float newPosition = overflowState.xTranslation + totalAmount / 2
+                                - totalDotLength / 2
+                                - mIconSize * 0.5f + mStaticDotRadius;
+                        mVisualOverflowAdaption = newPosition - visualOverflowStart;
+                        visualOverflowStart = newPosition;
+                    }
+                } else {
+                    visualOverflowStart += mVisualOverflowAdaption * (1f - mOpenedAmount);
+                }
+            }
+            translationX += iconState.iconAppearAmount * view.getWidth();
         }
-        if (overflowingIconIndex != -1) {
+        if (firstOverflowIndex != -1) {
             int numDots = 1;
-            View overflowIcon = getChildAt(overflowingIconIndex);
-            IconState overflowState = mIconStates.get(overflowIcon);
-            lastTwoIconWidth += overflowIcon.getWidth();
-            int dotWidth = mStaticDotRadius * 2 + mDotPadding;
-            int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding;
-            translationX = (getLayoutEnd() - lastTwoIconWidth / 2 - totalDotLength / 2)
-                    - overflowIcon.getWidth() * 0.3f + mStaticDotRadius;
-            float overflowStart = getLayoutEnd() - lastTwoIconWidth;
-            float overlapAmount = (overflowState.xTranslation - overflowStart)
-                    / overflowIcon.getWidth();
-            translationX += overlapAmount * dotWidth;
-            for (int i = overflowingIconIndex; i < childCount; i++) {
+            translationX = visualOverflowStart;
+            for (int i = firstOverflowIndex; i < childCount; i++) {
                 View view = getChildAt(i);
                 IconState iconState = mIconStates.get(view);
+                int dotWidth = mStaticDotRadius * 2 + mDotPadding;
                 iconState.xTranslation = translationX;
                 if (numDots <= 3) {
-                    iconState.visibleState = StatusBarIconView.STATE_DOT;
-                    translationX += numDots == 3 ? 3 * dotWidth : dotWidth;
+                    if (numDots == 1 && iconState.iconAppearAmount < 0.8f) {
+                        iconState.visibleState = StatusBarIconView.STATE_ICON;
+                        numDots--;
+                    } else {
+                        iconState.visibleState = StatusBarIconView.STATE_DOT;
+                    }
+                    translationX += (numDots == 3 ? 3 * dotWidth : dotWidth)
+                            * iconState.iconAppearAmount;
                 } else {
                     iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
                 }
@@ -250,14 +319,14 @@
     }
 
     private float getActualPaddingEnd() {
-        if (mActualPaddingEnd < 0) {
+        if (mActualPaddingEnd == NO_VALUE) {
             return getPaddingEnd();
         }
         return mActualPaddingEnd;
     }
 
     private float getActualPaddingStart() {
-        if (mActualPaddingStart < 0) {
+        if (mActualPaddingStart == NO_VALUE) {
             return getPaddingStart();
         }
         return mActualPaddingStart;
@@ -295,7 +364,7 @@
     }
 
     public int getActualWidth() {
-        if (mActualLayoutWidth < 0) {
+        if (mActualLayoutWidth == NO_VALUE) {
             return getWidth();
         }
         return mActualLayoutWidth;
@@ -305,28 +374,90 @@
         mChangingViewPositions = changingViewPositions;
     }
 
+    public IconState getIconState(StatusBarIconView icon) {
+        return mIconStates.get(icon);
+    }
+
+    public void setSpeedBumpIndex(int speedBumpIndex) {
+        mSpeedBumpIndex = speedBumpIndex;
+    }
+
+    public void setOpenedAmount(float expandAmount) {
+        mOpenedAmount = expandAmount;
+    }
+
+    public float getVisualOverflowAdaption() {
+        return mVisualOverflowAdaption;
+    }
+
+    public void setVisualOverflowAdaption(float visualOverflowAdaption) {
+        mVisualOverflowAdaption = visualOverflowAdaption;
+    }
+
+    public boolean hasOverflow() {
+        float width = (getChildCount() + OVERFLOW_EARLY_AMOUNT) * mIconSize;
+        return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0;
+    }
+
+    public int getIconSize() {
+        return mIconSize;
+    }
+
     public class IconState extends ViewState {
         public float iconAppearAmount = 1.0f;
+        public float clampedAppearAmount = 1.0f;
         public int visibleState;
         public boolean justAdded = true;
+        public boolean needsCannedAnimation;
+        public boolean keepClampedPosition;
+        public boolean useFullTransitionAmount;
+        public boolean translateContent;
 
         @Override
         public void applyToView(View view) {
             if (view instanceof StatusBarIconView) {
                 StatusBarIconView icon = (StatusBarIconView) view;
-                AnimationProperties animationProperties = DOT_ANIMATION_PROPERTIES;
+                boolean animate = false;
+                AnimationProperties animationProperties = null;
                 if (justAdded) {
                     super.applyToView(icon);
                     icon.setAlpha(0.0f);
                     icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */);
                     animationProperties = ADD_ICON_PROPERTIES;
+                    animate = true;
+                } else if (visibleState != icon.getVisibleState()) {
+                    animationProperties = DOT_ANIMATION_PROPERTIES;
+                    animate = true;
                 }
-                boolean animate = visibleState != icon.getVisibleState() || justAdded;
-                if (!animate && mAnimationStartIndex >= 0
+                if (!animate && mAddAnimationStartIndex >= 0
+                        && indexOfChild(view) >= mAddAnimationStartIndex
                         && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                             || visibleState != StatusBarIconView.STATE_HIDDEN)) {
-                    int viewIndex = indexOfChild(view);
-                    animate = viewIndex >= mAnimationStartIndex;
+                    animationProperties = DOT_ANIMATION_PROPERTIES;
+                    animate = true;
+                }
+                if (needsCannedAnimation) {
+                    AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
+                    animationFilter.reset();
+                    animationFilter.combineFilter(ICON_ANIMATION_PROPERTIES.getAnimationFilter());
+                    if (animationProperties != null) {
+                        animationFilter.combineFilter(animationProperties.getAnimationFilter());
+                    }
+                    animationProperties = mTempProperties;
+                    animationProperties.setDuration(CANNED_ANIMATION_DURATION);
+                    animate = true;
+                    mCannedAnimationStartIndex = indexOfChild(view);
+                }
+                if (!animate && mCannedAnimationStartIndex >= 0
+                        && indexOfChild(view) > mCannedAnimationStartIndex
+                        && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
+                        || visibleState != StatusBarIconView.STATE_HIDDEN)) {
+                    AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
+                    animationFilter.reset();
+                    animationFilter.animateX();
+                    animationProperties = mTempProperties;
+                    animationProperties.setDuration(CANNED_ANIMATION_DURATION);
+                    animate = true;
                 }
                 icon.setVisibleState(visibleState);
                 if (animate) {
@@ -336,6 +467,13 @@
                 }
             }
             justAdded = false;
+            needsCannedAnimation = false;
+        }
+
+        protected void onYTranslationAnimationFinished(View view) {
+            if (hidden) {
+                view.setVisibility(INVISIBLE);
+            }
         }
     }
 }
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 99e98f2..a239cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -105,7 +105,7 @@
     private boolean mAnimateNextTopPaddingChange;
 
     private int mTrackingPointer;
-    private VelocityTracker mVelocityTracker;
+    private VelocityTracker mQsVelocityTracker;
     private boolean mQsTracking;
 
     /**
@@ -208,6 +208,7 @@
     };
     private NotificationGroupManager mGroupManager;
     private boolean mOpening;
+    private int mIndicationBottomPadding;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -273,6 +274,8 @@
                 R.dimen.notification_panel_min_side_margin);
         mMaxFadeoutHeight = getResources().getDimensionPixelSize(
                 R.dimen.max_notification_fadeout_height);
+        mIndicationBottomPadding = getResources().getDimensionPixelSize(
+                R.dimen.keyguard_indication_bottom_padding);
     }
 
     public void updateResources() {
@@ -406,7 +409,8 @@
                 R.dimen.notification_divider_height));
         float shelfSize = mNotificationStackScroller.getNotificationShelf().getIntrinsicHeight()
                 + notificationPadding;
-        float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize;
+        float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize
+                - mIndicationBottomPadding;
         int count = 0;
         for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
             ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
@@ -684,7 +688,7 @@
     }
 
     private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
-        float vel = getCurrentVelocity();
+        float vel = getCurrentQSVelocity();
         final boolean expandsQs = flingExpandsQs(vel);
         if (expandsQs) {
             logQsSwipeDown(y);
@@ -693,7 +697,7 @@
     }
 
     private void logQsSwipeDown(float y) {
-        float vel = getCurrentVelocity();
+        float vel = getCurrentQSVelocity();
         final int gesture = mStatusBarState == StatusBarState.KEYGUARD
                 ? EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS
                 : EventLogConstants.SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS;
@@ -922,9 +926,9 @@
                     flingQsWithCurrentVelocity(y,
                             event.getActionMasked() == MotionEvent.ACTION_CANCEL);
                 }
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
+                if (mQsVelocityTracker != null) {
+                    mQsVelocityTracker.recycle();
+                    mQsVelocityTracker = null;
                 }
                 break;
         }
@@ -1286,24 +1290,24 @@
     }
 
     private void trackMovement(MotionEvent event) {
-        if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
+        if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
         mLastTouchX = event.getX();
         mLastTouchY = event.getY();
     }
 
     private void initVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
+        if (mQsVelocityTracker != null) {
+            mQsVelocityTracker.recycle();
         }
-        mVelocityTracker = VelocityTracker.obtain();
+        mQsVelocityTracker = VelocityTracker.obtain();
     }
 
-    private float getCurrentVelocity() {
-        if (mVelocityTracker == null) {
+    private float getCurrentQSVelocity() {
+        if (mQsVelocityTracker == null) {
             return 0;
         }
-        mVelocityTracker.computeCurrentVelocity(1000);
-        return mVelocityTracker.getYVelocity();
+        mQsVelocityTracker.computeCurrentVelocity(1000);
+        return mQsVelocityTracker.getYVelocity();
     }
 
     private void cancelQsAnimation() {
@@ -2278,6 +2282,9 @@
     }
 
     protected void updateExpandedHeight(float expandedHeight) {
+        if (mTracking) {
+            mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
+        }
         mNotificationStackScroller.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
         setOpening(expandedHeight <= getOpeningHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index f16c834..55dd578 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -306,6 +306,7 @@
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
+                trackMovement(event);
                 float h = y - mInitialTouchY;
 
                 // If the panel was collapsed when touching, we only need to check for the
@@ -346,8 +347,6 @@
                         !isTrackingBlocked()) {
                     setExpandedHeightInternal(newHeight);
                 }
-
-                trackMovement(event);
                 break;
 
             case MotionEvent.ACTION_UP:
@@ -449,6 +448,14 @@
         mPeekTouching = false;
     }
 
+    protected float getCurrentExpandVelocity() {
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getYVelocity();
+    }
+
     private int getFalsingThreshold() {
         float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
         return (int) (mUnlockFalsingThreshold * factor);
@@ -685,7 +692,7 @@
         mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
         ValueAnimator animator = createHeightAnimator(target);
         if (expand) {
-            if (expandBecauseOfFalsing) {
+            if (expandBecauseOfFalsing && vel < 0) {
                 vel = 0;
             }
             mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
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 2b74c84..ae9d068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
+
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -83,8 +84,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.Trace;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.Vibrator;
@@ -123,13 +124,13 @@
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DemoMode;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Interpolators;
-import com.android.keyguard.LatencyTracker;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
@@ -145,10 +146,12 @@
 import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
+import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
 import com.android.systemui.recents.events.activity.UndockingTaskEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -178,19 +181,20 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
-import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
 import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -319,19 +323,19 @@
     NetworkControllerImpl mNetworkController;
     HotspotControllerImpl mHotspotController;
     RotationLockControllerImpl mRotationLockController;
-    UserInfoController mUserInfoController;
+    UserInfoControllerImpl mUserInfoController;
     protected ZenModeController mZenModeController;
     CastControllerImpl mCastController;
     VolumeComponent mVolumeComponent;
     KeyguardUserSwitcher mKeyguardUserSwitcher;
-    FlashlightController mFlashlightController;
+    FlashlightControllerImpl mFlashlightController;
     protected UserSwitcherController mUserSwitcherController;
-    NextAlarmController mNextAlarmController;
-    protected KeyguardMonitor mKeyguardMonitor;
+    NextAlarmControllerImpl mNextAlarmController;
+    protected KeyguardMonitorImpl mKeyguardMonitor;
     BrightnessMirrorController mBrightnessMirrorController;
     AccessibilityController mAccessibilityController;
     protected FingerprintUnlockController mFingerprintUnlockController;
-    LightStatusBarController mLightStatusBarController;
+    LightBarController mLightBarController;
     protected LockscreenWallpaper mLockscreenWallpaper;
 
     int mNaturalBarHeight = -1;
@@ -827,10 +831,45 @@
             mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
         }
 
+        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
+        mKeyguardStatusView =
+                (KeyguardStatusView) mStatusBarWindow.findViewById(R.id.keyguard_status_view);
+        mKeyguardBottomArea =
+                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
+        mKeyguardBottomArea.setActivityStarter(this);
+        mKeyguardBottomArea.setAssistManager(mAssistManager);
+        mKeyguardIndicationController = new KeyguardIndicationController(mContext,
+                (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
+                mKeyguardBottomArea.getLockIcon());
+        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
+
+        // set the initial view visibility
+        setAreThereNotifications();
+
+        createIconController();
+
+        mBatteryController = createBatteryController();
+        mBatteryController.addCallback(new BatteryStateChangeCallback() {
+            @Override
+            public void onPowerSaveChanged(boolean isPowerSave) {
+                mHandler.post(mCheckBarModes);
+                if (mDozeServiceHost != null) {
+                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+                }
+            }
+            @Override
+            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+                // noop
+            }
+        });
+
+        mLightBarController = new LightBarController(mIconController, mNavigationBarView,
+                mBatteryController);
+
         ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
         View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
-        mScrimController = SystemUIFactory.getInstance().createScrimController(
+        mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
                 scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper);
         if (mScrimSrcModeEnabled) {
             Runnable runnable = new Runnable() {
@@ -849,23 +888,6 @@
         mStatusBarView.setScrimController(mScrimController);
         mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
 
-        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
-        mKeyguardStatusView =
-                (KeyguardStatusView) mStatusBarWindow.findViewById(R.id.keyguard_status_view);
-        mKeyguardBottomArea =
-                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
-        mKeyguardBottomArea.setActivityStarter(this);
-        mKeyguardBottomArea.setAssistManager(mAssistManager);
-        mKeyguardIndicationController = new KeyguardIndicationController(mContext,
-                (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
-                mKeyguardBottomArea.getLockIcon());
-        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
-
-        // set the initial view visibility
-        setAreThereNotifications();
-
-        createIconController();
-
         // Background thread for any controllers that need it.
         mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
         mHandlerThread.start();
@@ -873,20 +895,6 @@
         // Other icons
         mLocationController = new LocationControllerImpl(mContext,
                 mHandlerThread.getLooper()); // will post a notification
-        mBatteryController = createBatteryController();
-        mBatteryController.addCallback(new BatteryStateChangeCallback() {
-            @Override
-            public void onPowerSaveChanged(boolean isPowerSave) {
-                mHandler.post(mCheckBarModes);
-                if (mDozeServiceHost != null) {
-                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
-                }
-            }
-            @Override
-            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-                // noop
-            }
-        });
         mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper());
         mNetworkController.setUserSetupComplete(mUserSetup);
         mHotspotController = new HotspotControllerImpl(mContext);
@@ -895,7 +903,7 @@
         if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
             mRotationLockController = new RotationLockControllerImpl(mContext);
         }
-        mUserInfoController = new UserInfoController(mContext);
+        mUserInfoController = new UserInfoControllerImpl(mContext);
         mVolumeComponent = getComponent(VolumeComponent.class);
         if (mVolumeComponent != null) {
             mZenModeController = mVolumeComponent.getZenController();
@@ -906,16 +914,14 @@
         initSignalCluster(mKeyguardStatusBar);
         initEmergencyCryptkeeperText();
 
-        mFlashlightController = new FlashlightController(mContext);
+        mFlashlightController = new FlashlightControllerImpl(mContext);
         mKeyguardBottomArea.setFlashlightController(mFlashlightController);
         mKeyguardBottomArea.setPhoneStatusBar(this);
         mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
         mAccessibilityController = new AccessibilityController(mContext);
         mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
-        mNextAlarmController = new NextAlarmController(mContext);
-        mLightStatusBarController = new LightStatusBarController(mIconController,
-                mBatteryController);
-        mKeyguardMonitor = new KeyguardMonitor(mContext);
+        mNextAlarmController = new NextAlarmControllerImpl(mContext);
+        mKeyguardMonitor = new KeyguardMonitorImpl(mContext);
             mUserSwitcherController = createUserSwitcherController();
         if (UserManager.get(mContext).isUserSwitcherEnabled()) {
             createUserSwitcher();
@@ -1303,7 +1309,7 @@
         });
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
-        mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+        mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
         Trace.endSection();
     }
 
@@ -1358,6 +1364,14 @@
                 return false;
             }
 
+            ActivityManager.RunningTaskInfo runningTask =
+                    Recents.getSystemServices().getRunningTask();
+            boolean isRunningTaskInHomeOrRecentsStack = runningTask != null &&
+                    ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId);
+            if (isRunningTaskInHomeOrRecentsStack) {
+                return false;
+            }
+
             toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
                     MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
             return true;
@@ -3078,6 +3092,7 @@
                 Integer.toHexString(oldVal), Integer.toHexString(newVal),
                 Integer.toHexString(diff)));
         boolean sbModeChanged = false;
+        boolean nbModeChanged = false;
         if (diff != 0) {
             mSystemUiVisibility = newVal;
 
@@ -3101,7 +3116,7 @@
                     View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
                     View.NAVIGATION_BAR_TRANSPARENT);
             sbModeChanged = sbMode != -1;
-            final boolean nbModeChanged = nbMode != -1;
+            nbModeChanged = nbMode != -1;
             boolean checkBarModes = false;
             if (sbModeChanged && sbMode != mStatusBarMode) {
                 mStatusBarMode = sbMode;
@@ -3131,8 +3146,9 @@
             notifyUiVisibilityChanged(mSystemUiVisibility);
         }
 
-        mLightStatusBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
-                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
+        mLightBarController.onSystemUiVisibilityChanged(vis, fullscreenStackVis, dockedStackVis,
+                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode,
+                nbModeChanged, mNavigationBarMode);
     }
 
     protected int computeStatusBarMode(int oldVal, int newVal) {
@@ -3572,10 +3588,18 @@
             final boolean dismissShade,
             final boolean afterKeyguardGone,
             final boolean deferred) {
-        dismissKeyguardThenExecute(() -> {
+        final Runnable dismissAction = () -> {
             if (runnable != null) {
                 AsyncTask.execute(runnable);
             }
+        };
+        dismissKeyguardThenExecute(() -> {
+            if (mStatusBarKeyguardViewManager.isShowing()
+                    && mStatusBarKeyguardViewManager.isOccluded()) {
+                mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+            } else {
+                dismissAction.run();
+            }
             if (dismissShade) {
                 if (mExpandedVisible) {
                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
@@ -3664,8 +3688,6 @@
 
     private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
             boolean afterKeyguardGone) {
-        afterKeyguardGone |= mStatusBarKeyguardViewManager.isShowing()
-                && mStatusBarKeyguardViewManager.isOccluded();
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
                     afterKeyguardGone);
@@ -4187,8 +4209,14 @@
                                 onLaunchTransitionFadingEnded();
                             }
                         });
-                mIconController.appTransitionStarting(SystemClock.uptimeMillis(),
-                        StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION);
+                mIconController.getTransitionsController().appTransitionStarting(
+                        SystemClock.uptimeMillis(),
+                        LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION);
+                if (mNavigationBarView != null) {
+                    mNavigationBarView.getLightTransitionsController().appTransitionStarting(
+                            SystemClock.uptimeMillis(),
+                            LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION);
+                }
             }
         };
         if (mNotificationPanel.isLaunchTransitionRunning()) {
@@ -4319,7 +4347,10 @@
         // Treat Keyguard exit animation as an app transition to achieve nice transition for status
         // bar.
         mKeyguardGoingAway = true;
-        mIconController.appTransitionPending();
+        mIconController.getTransitionsController().appTransitionPending();
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getLightTransitionsController().appTransitionPending();
+        }
     }
 
     /**
@@ -4334,11 +4365,16 @@
         mKeyguardFadingAwayDelay = delay;
         mKeyguardFadingAwayDuration = fadeoutDuration;
         mWaitingForKeyguardExit = false;
-        mIconController.appTransitionStarting(
+        mIconController.getTransitionsController().appTransitionStarting(
                 startTime + fadeoutDuration
-                        - StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION,
-                StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION);
+                        - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
+                LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION);
         recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getLightTransitionsController().appTransitionStarting(
+                    startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
+                    LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION);
+        }
     }
 
     public boolean isKeyguardFadingAway() {
@@ -4559,6 +4595,7 @@
         mGroupManager.setStatusBarState(state);
         mFalsingManager.setStatusBarState(state);
         mStatusBarWindowManager.setStatusBarState(state);
+        mStackScroller.setStatusBarState(state);
         updateReportRejectedTouchVisibility();
         updateDozing();
     }
@@ -4968,13 +5005,19 @@
         // Use own timings when Keyguard is going away, see keyguardGoingAway and
         // setKeyguardFadingAway
         if (!mKeyguardFadingAway) {
-            mIconController.appTransitionPending();
+            mIconController.getTransitionsController().appTransitionPending();
+            if (mNavigationBarView != null) {
+                mNavigationBarView.getLightTransitionsController().appTransitionPending();
+            }
         }
     }
 
     @Override
     public void appTransitionCancelled() {
-        mIconController.appTransitionCancelled();
+        mIconController.getTransitionsController().appTransitionCancelled();
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getLightTransitionsController().appTransitionCancelled();
+        }
         EventBus.getDefault().send(new AppTransitionFinishedEvent());
     }
 
@@ -4984,7 +5027,11 @@
         // Use own timings when Keyguard is going away, see keyguardGoingAway and
         // setKeyguardFadingAway.
         if (!mKeyguardGoingAway) {
-            mIconController.appTransitionStarting(startTime, duration);
+            mIconController.getTransitionsController().appTransitionStarting(startTime, duration);
+            if (mNavigationBarView != null) {
+                mNavigationBarView.getLightTransitionsController().appTransitionStarting(
+                        startTime, duration);
+            }
         }
         if (mIconPolicy != null) {
             mIconPolicy.appTransitionStarting(startTime, duration);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 8fd6bbf..567ab3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -143,7 +143,7 @@
         mBattery = battery;
         mIconController = iconController;
         mNextAlarmController = nextAlarmController;
-        mProfileController = new ManagedProfileController(this);
+        mProfileController = new ManagedProfileControllerImpl(this);
 
         mHandlerThread = new HandlerThread(QSTileHost.class.getSimpleName(),
                 Process.THREAD_PRIORITY_BACKGROUND);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 28aed87..08a91bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -32,6 +32,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.keyguard.KeyguardStatusView;
@@ -236,10 +237,12 @@
     }
 
     @Override
-    protected void onDetachedFromWindow() {
+    @VisibleForTesting
+    public void onDetachedFromWindow() {
         setListening(false);
         mHost.getUserInfoController().removeCallback(this);
         mHost.getNetworkController().removeEmergencyListener(this);
+        mHost.getUserInfoController().removeCallback(this);
         super.onDetachedFromWindow();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0e74e57..944495e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -59,6 +59,7 @@
     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
 
+    private final LightBarController mLightBarController;
     protected final ScrimView mScrimBehind;
     private final ScrimView mScrimInFront;
     private final UnlockMethodCache mUnlockMethodCache;
@@ -99,13 +100,15 @@
     private boolean mKeyguardFadingOutInProgress;
     private ValueAnimator mKeyguardFadeoutAnimation;
 
-    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim) {
+    public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
+            ScrimView scrimInFront, View headsUpScrim) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
         mHeadsUpScrim = headsUpScrim;
         final Context context = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+        mLightBarController = lightBarController;
         updateHeadsUpScrim(false);
     }
 
@@ -341,6 +344,7 @@
             alpha = Math.max(0.0f, Math.min(1.0f, alpha));
             mCurrentHeadsUpAlpha = alpha;
         }
+        mLightBarController.setScrimAlpha(mCurrentBehindAlpha);
     }
 
     protected void updateScrimColor(View scrim) {
@@ -556,10 +560,6 @@
         mScrimBehind.setExcludedArea(area);
     }
 
-    public void setLeftInset(int inset) {
-        mScrimBehind.setLeftInset(inset);
-    }
-
     public int getScrimBehindColor() {
         return mScrimBehind.getScrimColorWithAlpha();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index a948a08..a0425e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -60,7 +60,6 @@
  */
 public class StatusBarIconController extends StatusBarIconList implements Tunable {
 
-    public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
     public static final String ICON_BLACKLIST = "icon_blacklist";
     public static final int DEFAULT_ICON_TINT = Color.WHITE;
 
@@ -90,31 +89,16 @@
     private static final Rect sTmpRect = new Rect();
     private static final int[] sTmpInt2 = new int[2];
 
-    private boolean mTransitionPending;
-    private boolean mTintChangePending;
-    private float mPendingDarkIntensity;
-    private ValueAnimator mTintAnimator;
-
     private int mDarkModeIconColorSingleTone;
     private int mLightModeIconColorSingleTone;
 
-    private final Handler mHandler;
-    private boolean mTransitionDeferring;
-    private long mTransitionDeferringStartTime;
-    private long mTransitionDeferringDuration;
+    private final LightBarTransitionsController mTransitionsController;
 
     private boolean mClockVisibleByPolicy = true;
     private boolean mClockVisibleByUser = true;
 
     private final ArraySet<String> mIconBlacklist = new ArraySet<>();
 
-    private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mTransitionDeferring = false;
-        }
-    };
-
     public StatusBarIconController(Context context, View statusBar, View keyguardStatusBar,
             PhoneStatusBar phoneStatusBar) {
         super(context.getResources().getStringArray(
@@ -144,16 +128,21 @@
         mClock = (TextView) statusBar.findViewById(R.id.clock);
         mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
         mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
-        mHandler = new Handler();
         loadDimens();
 
         TunerService.get(mContext).addTunable(this, ICON_BLACKLIST);
+
+        mTransitionsController = new LightBarTransitionsController(this::setIconTintInternal);
     }
 
     public void setSignalCluster(SignalClusterView signalCluster) {
         mSignalCluster = signalCluster;
     }
 
+    public LightBarTransitionsController getTransitionsController() {
+        return mTransitionsController;
+    }
+
     /**
      * Looks up the scale factor for status bar icons and scales the battery view by that amount.
      */
@@ -444,41 +433,6 @@
         mNotificationIconAreaController.setTintArea(darkArea);
     }
 
-    public void setIconsDark(boolean dark, boolean animate) {
-        if (!animate) {
-            setIconTintInternal(dark ? 1.0f : 0.0f);
-        } else if (mTransitionPending) {
-            deferIconTintChange(dark ? 1.0f : 0.0f);
-        } else if (mTransitionDeferring) {
-            animateIconTint(dark ? 1.0f : 0.0f,
-                    Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
-                    mTransitionDeferringDuration);
-        } else {
-            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
-        }
-    }
-
-    private void animateIconTint(float targetDarkIntensity, long delay,
-            long duration) {
-        if (mTintAnimator != null) {
-            mTintAnimator.cancel();
-        }
-        if (mDarkIntensity == targetDarkIntensity) {
-            return;
-        }
-        mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity);
-        mTintAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                setIconTintInternal((Float) animation.getAnimatedValue());
-            }
-        });
-        mTintAnimator.setDuration(duration);
-        mTintAnimator.setStartDelay(delay);
-        mTintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mTintAnimator.start();
-    }
-
     private void setIconTintInternal(float darkIntensity) {
         mDarkIntensity = darkIntensity;
         mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
@@ -487,14 +441,6 @@
         applyIconTint();
     }
 
-    private void deferIconTintChange(float darkIntensity) {
-        if (mTintChangePending && darkIntensity == mPendingDarkIntensity) {
-            return;
-        }
-        mTintChangePending = true;
-        mPendingDarkIntensity = darkIntensity;
-    }
-
     /**
      * @return the tint to apply to {@param view} depending on the desired tint {@param color} and
      *         the screen {@param tintArea} in which to apply that tint
@@ -551,38 +497,6 @@
         mClock.setTextColor(getTint(mTintArea, mClock, mIconTint));
     }
 
-    public void appTransitionPending() {
-        mTransitionPending = true;
-    }
-
-    public void appTransitionCancelled() {
-        if (mTransitionPending && mTintChangePending) {
-            mTintChangePending = false;
-            animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
-        }
-        mTransitionPending = false;
-    }
-
-    public void appTransitionStarting(long startTime, long duration) {
-        if (mTransitionPending && mTintChangePending) {
-            mTintChangePending = false;
-            animateIconTint(mPendingDarkIntensity,
-                    Math.max(0, startTime - SystemClock.uptimeMillis()),
-                    duration);
-
-        } else if (mTransitionPending) {
-
-            // If we don't have a pending tint change yet, the change might come in the future until
-            // startTime is reached.
-            mTransitionDeferring = true;
-            mTransitionDeferringStartTime = startTime;
-            mTransitionDeferringDuration = duration;
-            mHandler.removeCallbacks(mTransitionDeferringDoneRunnable);
-            mHandler.postAtTime(mTransitionDeferringDoneRunnable, startTime);
-        }
-        mTransitionPending = false;
-    }
-
     public static ArraySet<String> getIconBlacklist(String blackListStr) {
         ArraySet<String> ret = new ArraySet<String>();
         if (blackListStr == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4263670..2e279b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -40,6 +40,8 @@
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.systemui.statusbar.phone.FingerprintUnlockController.*;
 
+import java.util.ArrayList;
+
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -90,6 +92,7 @@
     protected boolean mLastRemoteInputActive;
 
     private OnDismissAction mAfterKeyguardGoneAction;
+    private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
     private boolean mDeviceWillWakeUp;
     private boolean mDeferScrimFadeOut;
 
@@ -165,6 +168,13 @@
     }
 
     /**
+     * Adds a {@param runnable} to be executed after Keyguard is gone.
+     */
+    public void addAfterKeyguardGoneRunnable(Runnable runnable) {
+        mAfterKeyguardGoneRunnables.add(runnable);
+    }
+
+    /**
      * Reset the state of the view.
      */
     public void reset(boolean hideBouncerWhenShowing) {
@@ -418,6 +428,10 @@
             mAfterKeyguardGoneAction.onDismiss();
             mAfterKeyguardGoneAction = null;
         }
+        for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
+            mAfterKeyguardGoneRunnables.get(i).run();
+        }
+        mAfterKeyguardGoneRunnables.clear();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index f6dd88d9..487f0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -130,7 +130,6 @@
     }
 
     private void applyMargins() {
-        mService.mScrimController.setLeftInset(mLeftInset);
         final int N = getChildCount();
         for (int i = 0; i < N; i++) {
             View child = getChildAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index e5f1e68..0df7859 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -14,92 +14,14 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.content.Context;
-import android.net.INetworkPolicyListener;
-import android.net.NetworkPolicyManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 
-import java.util.ArrayList;
+public interface DataSaverController extends CallbackController<Listener> {
 
-public class DataSaverController implements CallbackController<Listener> {
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final ArrayList<Listener> mListeners = new ArrayList<>();
-    private final NetworkPolicyManager mPolicyManager;
-
-    public DataSaverController(Context context) {
-        mPolicyManager = NetworkPolicyManager.from(context);
-    }
-
-    private void handleRestrictBackgroundChanged(boolean isDataSaving) {
-        synchronized (mListeners) {
-            for (int i = 0; i < mListeners.size(); i++) {
-                mListeners.get(i).onDataSaverChanged(isDataSaving);
-            }
-        }
-    }
-
-    public void addCallback(Listener listener) {
-        synchronized (mListeners) {
-            mListeners.add(listener);
-            if (mListeners.size() == 1) {
-                mPolicyManager.registerListener(mPolicyListener);
-            }
-        }
-        listener.onDataSaverChanged(isDataSaverEnabled());
-    }
-
-    public void removeCallback(Listener listener) {
-        synchronized (mListeners) {
-            mListeners.remove(listener);
-            if (mListeners.size() == 0) {
-                mPolicyManager.unregisterListener(mPolicyListener);
-            }
-        }
-    }
-
-    public boolean isDataSaverEnabled() {
-        return mPolicyManager.getRestrictBackground();
-    }
-
-    public void setDataSaverEnabled(boolean enabled) {
-        mPolicyManager.setRestrictBackground(enabled);
-        try {
-            mPolicyListener.onRestrictBackgroundChanged(enabled);
-        } catch (RemoteException e) {
-        }
-    }
-
-    private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
-        @Override
-        public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
-        }
-
-        @Override
-        public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
-        }
-
-        @Override
-        public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleRestrictBackgroundChanged(isDataSaving);
-                }
-            });
-        }
-
-        @Override
-        public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException {
-        }
-    };
+    boolean isDataSaverEnabled();
+    void setDataSaverEnabled(boolean enabled);
 
     public interface Listener {
         void onDataSaverChanged(boolean isDataSaving);
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
new file mode 100644
index 0000000..2951943
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.net.INetworkPolicyListener;
+import android.net.NetworkPolicyManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+
+import java.util.ArrayList;
+
+public class DataSaverControllerImpl implements DataSaverController {
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final ArrayList<Listener> mListeners = new ArrayList<>();
+    private final NetworkPolicyManager mPolicyManager;
+
+    public DataSaverControllerImpl(Context context) {
+        mPolicyManager = NetworkPolicyManager.from(context);
+    }
+
+    private void handleRestrictBackgroundChanged(boolean isDataSaving) {
+        synchronized (mListeners) {
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onDataSaverChanged(isDataSaving);
+            }
+        }
+    }
+
+    public void addCallback(Listener listener) {
+        synchronized (mListeners) {
+            mListeners.add(listener);
+            if (mListeners.size() == 1) {
+                mPolicyManager.registerListener(mPolicyListener);
+            }
+        }
+        listener.onDataSaverChanged(isDataSaverEnabled());
+    }
+
+    public void removeCallback(Listener listener) {
+        synchronized (mListeners) {
+            mListeners.remove(listener);
+            if (mListeners.size() == 0) {
+                mPolicyManager.unregisterListener(mPolicyListener);
+            }
+        }
+    }
+
+    public boolean isDataSaverEnabled() {
+        return mPolicyManager.getRestrictBackground();
+    }
+
+    public void setDataSaverEnabled(boolean enabled) {
+        mPolicyManager.setRestrictBackground(enabled);
+        try {
+            mPolicyListener.onRestrictBackgroundChanged(enabled);
+        } catch (RemoteException e) {
+        }
+    }
+
+    private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+        @Override
+        public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
+        }
+
+        @Override
+        public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
+        }
+
+        @Override
+        public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleRestrictBackgroundChanged(isDataSaving);
+                }
+            });
+        }
+
+        @Override
+        public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException {
+        }
+    };
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index 8abfb89..a2d1baf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -41,12 +42,20 @@
 public class EmergencyCryptkeeperText extends TextView {
 
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+    private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onPhoneStateChanged(int phoneState) {
             update();
         }
     };
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
+                update();
+            }
+        }
+    };
 
     public EmergencyCryptkeeperText(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -58,6 +67,8 @@
         super.onAttachedToWindow();
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         mKeyguardUpdateMonitor.registerCallback(mCallback);
+        getContext().registerReceiver(mReceiver,
+                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
         update();
     }
 
@@ -67,6 +78,7 @@
         if (mKeyguardUpdateMonitor != null) {
             mKeyguardUpdateMonitor.removeCallback(mCallback);
         }
+        getContext().unregisterReceiver(mReceiver);
     }
 
     public void update() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index 0f77b03..6023f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -1,255 +1,27 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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.statusbar.policy;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.text.TextUtils;
-import android.util.Log;
-
 import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
+public interface FlashlightController extends CallbackController<FlashlightListener> {
 
-/**
- * Manages the flashlight.
- */
-public class FlashlightController implements CallbackController<FlashlightListener> {
-
-    private static final String TAG = "FlashlightController";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final int DISPATCH_ERROR = 0;
-    private static final int DISPATCH_CHANGED = 1;
-    private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
-
-    private final CameraManager mCameraManager;
-    private final Context mContext;
-    /** Call {@link #ensureHandler()} before using */
-    private Handler mHandler;
-
-    /** Lock on mListeners when accessing */
-    private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
-
-    /** Lock on {@code this} when accessing */
-    private boolean mFlashlightEnabled;
-
-    private String mCameraId;
-    private boolean mTorchAvailable;
-
-    public FlashlightController(Context context) {
-        mContext = context;
-        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-
-        tryInitCamera();
-    }
-
-    private void tryInitCamera() {
-        try {
-            mCameraId = getCameraId();
-        } catch (Throwable e) {
-            Log.e(TAG, "Couldn't initialize.", e);
-            return;
-        }
-
-        if (mCameraId != null) {
-            ensureHandler();
-            mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
-        }
-    }
-
-    public void setFlashlight(boolean enabled) {
-        boolean pendingError = false;
-        synchronized (this) {
-            if (mCameraId == null) return;
-            if (mFlashlightEnabled != enabled) {
-                mFlashlightEnabled = enabled;
-                try {
-                    mCameraManager.setTorchMode(mCameraId, enabled);
-                } catch (CameraAccessException e) {
-                    Log.e(TAG, "Couldn't set torch mode", e);
-                    mFlashlightEnabled = false;
-                    pendingError = true;
-                }
-            }
-        }
-        dispatchModeChanged(mFlashlightEnabled);
-        if (pendingError) {
-            dispatchError();
-        }
-    }
-
-    public boolean hasFlashlight() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
-    }
-
-    public synchronized boolean isEnabled() {
-        return mFlashlightEnabled;
-    }
-
-    public synchronized boolean isAvailable() {
-        return mTorchAvailable;
-    }
-
-    public void addCallback(FlashlightListener l) {
-        synchronized (mListeners) {
-            if (mCameraId == null) {
-                tryInitCamera();
-            }
-            cleanUpListenersLocked(l);
-            mListeners.add(new WeakReference<>(l));
-        }
-    }
-
-    public void removeCallback(FlashlightListener l) {
-        synchronized (mListeners) {
-            cleanUpListenersLocked(l);
-        }
-    }
-
-    private synchronized void ensureHandler() {
-        if (mHandler == null) {
-            HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
-            thread.start();
-            mHandler = new Handler(thread.getLooper());
-        }
-    }
-
-    private String getCameraId() throws CameraAccessException {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (String id : ids) {
-            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
-            Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
-            Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
-            if (flashAvailable != null && flashAvailable
-                    && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
-                return id;
-            }
-        }
-        return null;
-    }
-
-    private void dispatchModeChanged(boolean enabled) {
-        dispatchListeners(DISPATCH_CHANGED, enabled);
-    }
-
-    private void dispatchError() {
-        dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
-    }
-
-    private void dispatchAvailabilityChanged(boolean available) {
-        dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
-    }
-
-    private void dispatchListeners(int message, boolean argument) {
-        synchronized (mListeners) {
-            final int N = mListeners.size();
-            boolean cleanup = false;
-            for (int i = 0; i < N; i++) {
-                FlashlightListener l = mListeners.get(i).get();
-                if (l != null) {
-                    if (message == DISPATCH_ERROR) {
-                        l.onFlashlightError();
-                    } else if (message == DISPATCH_CHANGED) {
-                        l.onFlashlightChanged(argument);
-                    } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
-                        l.onFlashlightAvailabilityChanged(argument);
-                    }
-                } else {
-                    cleanup = true;
-                }
-            }
-            if (cleanup) {
-                cleanUpListenersLocked(null);
-            }
-        }
-    }
-
-    private void cleanUpListenersLocked(FlashlightListener listener) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            FlashlightListener found = mListeners.get(i).get();
-            if (found == null || found == listener) {
-                mListeners.remove(i);
-            }
-        }
-    }
-
-    private final CameraManager.TorchCallback mTorchCallback =
-            new CameraManager.TorchCallback() {
-
-        @Override
-        public void onTorchModeUnavailable(String cameraId) {
-            if (TextUtils.equals(cameraId, mCameraId)) {
-                setCameraAvailable(false);
-            }
-        }
-
-        @Override
-        public void onTorchModeChanged(String cameraId, boolean enabled) {
-            if (TextUtils.equals(cameraId, mCameraId)) {
-                setCameraAvailable(true);
-                setTorchMode(enabled);
-            }
-        }
-
-        private void setCameraAvailable(boolean available) {
-            boolean changed;
-            synchronized (FlashlightController.this) {
-                changed = mTorchAvailable != available;
-                mTorchAvailable = available;
-            }
-            if (changed) {
-                if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
-                dispatchAvailabilityChanged(available);
-            }
-        }
-
-        private void setTorchMode(boolean enabled) {
-            boolean changed;
-            synchronized (FlashlightController.this) {
-                changed = mFlashlightEnabled != enabled;
-                mFlashlightEnabled = enabled;
-            }
-            if (changed) {
-                if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
-                dispatchModeChanged(enabled);
-            }
-        }
-    };
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("FlashlightController state:");
-
-        pw.print("  mCameraId=");
-        pw.println(mCameraId);
-        pw.print("  mFlashlightEnabled=");
-        pw.println(mFlashlightEnabled);
-        pw.print("  mTorchAvailable=");
-        pw.println(mTorchAvailable);
-    }
+    boolean hasFlashlight();
+    void setFlashlight(boolean newState);
+    boolean isAvailable();
+    boolean isEnabled();
 
     public interface FlashlightListener {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
new file mode 100644
index 0000000..008d837
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Manages the flashlight.
+ */
+public class FlashlightControllerImpl implements FlashlightController {
+
+    private static final String TAG = "FlashlightController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final int DISPATCH_ERROR = 0;
+    private static final int DISPATCH_CHANGED = 1;
+    private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
+
+    private final CameraManager mCameraManager;
+    private final Context mContext;
+    /** Call {@link #ensureHandler()} before using */
+    private Handler mHandler;
+
+    /** Lock on mListeners when accessing */
+    private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
+
+    /** Lock on {@code this} when accessing */
+    private boolean mFlashlightEnabled;
+
+    private String mCameraId;
+    private boolean mTorchAvailable;
+
+    public FlashlightControllerImpl(Context context) {
+        mContext = context;
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+
+        tryInitCamera();
+    }
+
+    private void tryInitCamera() {
+        try {
+            mCameraId = getCameraId();
+        } catch (Throwable e) {
+            Log.e(TAG, "Couldn't initialize.", e);
+            return;
+        }
+
+        if (mCameraId != null) {
+            ensureHandler();
+            mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
+        }
+    }
+
+    public void setFlashlight(boolean enabled) {
+        boolean pendingError = false;
+        synchronized (this) {
+            if (mCameraId == null) return;
+            if (mFlashlightEnabled != enabled) {
+                mFlashlightEnabled = enabled;
+                try {
+                    mCameraManager.setTorchMode(mCameraId, enabled);
+                } catch (CameraAccessException e) {
+                    Log.e(TAG, "Couldn't set torch mode", e);
+                    mFlashlightEnabled = false;
+                    pendingError = true;
+                }
+            }
+        }
+        dispatchModeChanged(mFlashlightEnabled);
+        if (pendingError) {
+            dispatchError();
+        }
+    }
+
+    public boolean hasFlashlight() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
+    }
+
+    public synchronized boolean isEnabled() {
+        return mFlashlightEnabled;
+    }
+
+    public synchronized boolean isAvailable() {
+        return mTorchAvailable;
+    }
+
+    public void addCallback(FlashlightListener l) {
+        synchronized (mListeners) {
+            if (mCameraId == null) {
+                tryInitCamera();
+            }
+            cleanUpListenersLocked(l);
+            mListeners.add(new WeakReference<>(l));
+        }
+    }
+
+    public void removeCallback(FlashlightListener l) {
+        synchronized (mListeners) {
+            cleanUpListenersLocked(l);
+        }
+    }
+
+    private synchronized void ensureHandler() {
+        if (mHandler == null) {
+            HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+            thread.start();
+            mHandler = new Handler(thread.getLooper());
+        }
+    }
+
+    private String getCameraId() throws CameraAccessException {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (String id : ids) {
+            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
+            Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+            Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
+            if (flashAvailable != null && flashAvailable
+                    && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+                return id;
+            }
+        }
+        return null;
+    }
+
+    private void dispatchModeChanged(boolean enabled) {
+        dispatchListeners(DISPATCH_CHANGED, enabled);
+    }
+
+    private void dispatchError() {
+        dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
+    }
+
+    private void dispatchAvailabilityChanged(boolean available) {
+        dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
+    }
+
+    private void dispatchListeners(int message, boolean argument) {
+        synchronized (mListeners) {
+            final int N = mListeners.size();
+            boolean cleanup = false;
+            for (int i = 0; i < N; i++) {
+                FlashlightListener l = mListeners.get(i).get();
+                if (l != null) {
+                    if (message == DISPATCH_ERROR) {
+                        l.onFlashlightError();
+                    } else if (message == DISPATCH_CHANGED) {
+                        l.onFlashlightChanged(argument);
+                    } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
+                        l.onFlashlightAvailabilityChanged(argument);
+                    }
+                } else {
+                    cleanup = true;
+                }
+            }
+            if (cleanup) {
+                cleanUpListenersLocked(null);
+            }
+        }
+    }
+
+    private void cleanUpListenersLocked(FlashlightListener listener) {
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            FlashlightListener found = mListeners.get(i).get();
+            if (found == null || found == listener) {
+                mListeners.remove(i);
+            }
+        }
+    }
+
+    private final CameraManager.TorchCallback mTorchCallback =
+            new CameraManager.TorchCallback() {
+
+        @Override
+        public void onTorchModeUnavailable(String cameraId) {
+            if (TextUtils.equals(cameraId, mCameraId)) {
+                setCameraAvailable(false);
+            }
+        }
+
+        @Override
+        public void onTorchModeChanged(String cameraId, boolean enabled) {
+            if (TextUtils.equals(cameraId, mCameraId)) {
+                setCameraAvailable(true);
+                setTorchMode(enabled);
+            }
+        }
+
+        private void setCameraAvailable(boolean available) {
+            boolean changed;
+            synchronized (FlashlightControllerImpl.this) {
+                changed = mTorchAvailable != available;
+                mTorchAvailable = available;
+            }
+            if (changed) {
+                if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
+                dispatchAvailabilityChanged(available);
+            }
+        }
+
+        private void setTorchMode(boolean enabled) {
+            boolean changed;
+            synchronized (FlashlightControllerImpl.this) {
+                changed = mFlashlightEnabled != enabled;
+                mFlashlightEnabled = enabled;
+            }
+            if (changed) {
+                if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
+                dispatchModeChanged(enabled);
+            }
+        }
+    };
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("FlashlightController state:");
+
+        pw.print("  mCameraId=");
+        pw.println(mCameraId);
+        pw.print("  mFlashlightEnabled=");
+        pw.println(mFlashlightEnabled);
+        pw.print("  mTorchAvailable=");
+        pw.println(mTorchAvailable);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
new file mode 100644
index 0000000..3ee01de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+/**
+ * Drawable for {@link KeyButtonView}s which contains an asset for both normal mode and light
+ * navigation bar mode.
+ */
+public class KeyButtonDrawable extends LayerDrawable {
+
+    private final boolean mHasDarkDrawable;
+
+    public static KeyButtonDrawable create(Drawable lightDrawable,
+            @Nullable Drawable darkDrawable) {
+        if (darkDrawable != null) {
+            return new KeyButtonDrawable(
+                    new Drawable[] { lightDrawable.mutate(), darkDrawable.mutate() });
+        } else {
+            return new KeyButtonDrawable(new Drawable[] { lightDrawable.mutate() });
+        }
+    }
+
+    private KeyButtonDrawable(Drawable[] drawables) {
+        super(drawables);
+        mutate();
+        mHasDarkDrawable = drawables.length > 1;
+        setDarkIntensity(0f);
+    }
+
+    public void setDarkIntensity(float intensity) {
+        if (!mHasDarkDrawable) {
+            return;
+        }
+        getDrawable(0).setAlpha((int) ((1 - intensity) * 255f));
+        getDrawable(1).setAlpha((int) (intensity * 255f));
+        invalidateSelf();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 57e092a..8e51ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -41,6 +41,7 @@
 
     private static final float GLOW_MAX_SCALE_FACTOR = 1.35f;
     private static final float GLOW_MAX_ALPHA = 0.2f;
+    private static final float GLOW_MAX_ALPHA_DARK = 0.1f;
     private static final int ANIMATION_DURATION_SCALE = 350;
     private static final int ANIMATION_DURATION_FADE = 450;
 
@@ -57,6 +58,8 @@
     private boolean mPressed;
     private boolean mDrawingHardwareGlow;
     private int mMaxWidth;
+    private boolean mLastDark;
+    private boolean mDark;
 
     private final Interpolator mInterpolator = new LogInterpolator();
     private boolean mSupportHardware;
@@ -70,11 +73,15 @@
         mTargetView = targetView;
     }
 
+    public void setDarkIntensity(float darkIntensity) {
+        mDark = darkIntensity >= 0.5f;
+    }
+
     private Paint getRipplePaint() {
         if (mRipplePaint == null) {
             mRipplePaint = new Paint();
             mRipplePaint.setAntiAlias(true);
-            mRipplePaint.setColor(0xffffffff);
+            mRipplePaint.setColor(mLastDark ? 0xff000000 : 0xffffffff);
         }
         return mRipplePaint;
     }
@@ -155,6 +162,10 @@
         invalidateSelf();
     }
 
+    private float getMaxGlowAlpha() {
+        return mLastDark ? GLOW_MAX_ALPHA_DARK : GLOW_MAX_ALPHA;
+    }
+
     @Override
     protected boolean onStateChange(int[] state) {
         boolean pressed = false;
@@ -184,6 +195,10 @@
     }
 
     public void setPressed(boolean pressed) {
+        if (mDark != mLastDark && pressed) {
+            mRipplePaint = null;
+            mLastDark = mDark;
+        }
         if (mSupportHardware) {
             setPressedHardware(pressed);
         } else {
@@ -212,7 +227,7 @@
 
     private void enterSoftware() {
         cancelAnimations();
-        mGlowAlpha = GLOW_MAX_ALPHA;
+        mGlowAlpha = getMaxGlowAlpha();
         ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
                 0f, GLOW_MAX_SCALE_FACTOR);
         scaleAnimator.setInterpolator(mInterpolator);
@@ -312,7 +327,7 @@
         }
 
         mGlowScale = GLOW_MAX_SCALE_FACTOR;
-        mGlowAlpha = GLOW_MAX_ALPHA;
+        mGlowAlpha = getMaxGlowAlpha();
         mRipplePaint = getRipplePaint();
         mRipplePaint.setAlpha((int) (mGlowAlpha * 255));
         mPaintProp = CanvasProperty.createPaint(mRipplePaint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index ae59315..45cfbdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -60,6 +58,7 @@
     private boolean mGestureAborted;
     private boolean mLongClicked;
     private OnClickListener mOnClickListener;
+    private final KeyButtonRipple mRipple;
 
     private final Runnable mCheckLongPress = new Runnable() {
         public void run() {
@@ -99,11 +98,12 @@
 
         a.recycle();
 
-
         setClickable(true);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        setBackground(new KeyButtonRipple(context, this));
+
+        mRipple = new KeyButtonRipple(context, this);
+        setBackground(mRipple);
     }
 
     public void setCode(int code) {
@@ -265,13 +265,16 @@
     }
 
     @Override
-    public void setImageResource(@DrawableRes int resId) {
-        super.setImageResource(resId);
-    }
+    public void setDarkIntensity(float darkIntensity) {
+        Drawable drawable = getDrawable();
+        if (drawable != null) {
+            ((KeyButtonDrawable) getDrawable()).setDarkIntensity(darkIntensity);
 
-    @Override
-    public void setImageDrawable(@Nullable Drawable drawable) {
-        super.setImageDrawable(drawable);
+            // Since we reuse the same drawable for multiple views, we need to invalidate the view
+            // manually.
+            invalidate();
+        }
+        mRipple.setDarkIntensity(darkIntensity);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 0396613..de47267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -1,126 +1,28 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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.
+ * 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.statusbar.policy;
 
-import android.app.ActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
-import android.view.WindowManagerGlobal;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
 
-import java.util.ArrayList;
+public interface KeyguardMonitor extends CallbackController<Callback> {
 
-public class KeyguardMonitor extends KeyguardUpdateMonitorCallback
-        implements CallbackController<Callback> {
-
-    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
-
-    private final Context mContext;
-    private final CurrentUserTracker mUserTracker;
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
-    private int mCurrentUser;
-    private boolean mShowing;
-    private boolean mSecure;
-    private boolean mOccluded;
-    private boolean mCanSkipBouncer;
-
-    private boolean mListening;
-
-    public KeyguardMonitor(Context context) {
-        mContext = context;
-        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-        mUserTracker = new CurrentUserTracker(mContext) {
-            @Override
-            public void onUserSwitched(int newUserId) {
-                mCurrentUser = newUserId;
-                updateCanSkipBouncerState();
-            }
-        };
-    }
-
-    public void addCallback(Callback callback) {
-        mCallbacks.add(callback);
-        if (mCallbacks.size() != 0 && !mListening) {
-            mListening = true;
-            mCurrentUser = ActivityManager.getCurrentUser();
-            updateCanSkipBouncerState();
-            mKeyguardUpdateMonitor.registerCallback(this);
-            mUserTracker.startTracking();
-        }
-    }
-
-    public void removeCallback(Callback callback) {
-        if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
-            mListening = false;
-            mKeyguardUpdateMonitor.removeCallback(this);
-            mUserTracker.stopTracking();
-        }
-    }
-
-    public boolean isShowing() {
-        return mShowing;
-    }
-
-    public boolean isSecure() {
-        return mSecure;
-    }
-
-    public boolean isOccluded() {
-        return mOccluded;
-    }
-
-    public boolean canSkipBouncer() {
-        return mCanSkipBouncer;
-    }
-
-    public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
-        if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
-        mShowing = showing;
-        mSecure = secure;
-        mOccluded = occluded;
-        notifyKeyguardChanged();
-    }
-
-    @Override
-    public void onTrustChanged(int userId) {
-        updateCanSkipBouncerState();
-        notifyKeyguardChanged();
-    }
-
-    public boolean isDeviceInteractive() {
-        return mKeyguardUpdateMonitor.isDeviceInteractive();
-    }
-
-    private void updateCanSkipBouncerState() {
-        mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser);
-    }
-
-    private void notifyKeyguardChanged() {
-        for (Callback callback : mCallbacks) {
-            callback.onKeyguardChanged();
-        }
-    }
+    boolean isSecure();
+    boolean canSkipBouncer();
+    boolean isShowing();
 
     public interface Callback {
         void onKeyguardChanged();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
new file mode 100644
index 0000000..769f93f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.WindowManagerGlobal;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
+
+import java.util.ArrayList;
+
+public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
+        implements KeyguardMonitor {
+
+    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+
+    private final Context mContext;
+    private final CurrentUserTracker mUserTracker;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    private int mCurrentUser;
+    private boolean mShowing;
+    private boolean mSecure;
+    private boolean mOccluded;
+    private boolean mCanSkipBouncer;
+
+    private boolean mListening;
+
+    public KeyguardMonitorImpl(Context context) {
+        mContext = context;
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                mCurrentUser = newUserId;
+                updateCanSkipBouncerState();
+            }
+        };
+    }
+
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
+        if (mCallbacks.size() != 0 && !mListening) {
+            mListening = true;
+            mCurrentUser = ActivityManager.getCurrentUser();
+            updateCanSkipBouncerState();
+            mKeyguardUpdateMonitor.registerCallback(this);
+            mUserTracker.startTracking();
+        }
+    }
+
+    public void removeCallback(Callback callback) {
+        if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
+            mListening = false;
+            mKeyguardUpdateMonitor.removeCallback(this);
+            mUserTracker.stopTracking();
+        }
+    }
+
+    public boolean isShowing() {
+        return mShowing;
+    }
+
+    public boolean isSecure() {
+        return mSecure;
+    }
+
+    public boolean isOccluded() {
+        return mOccluded;
+    }
+
+    public boolean canSkipBouncer() {
+        return mCanSkipBouncer;
+    }
+
+    public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
+        if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
+        mShowing = showing;
+        mSecure = secure;
+        mOccluded = occluded;
+        notifyKeyguardChanged();
+    }
+
+    @Override
+    public void onTrustChanged(int userId) {
+        updateCanSkipBouncerState();
+        notifyKeyguardChanged();
+    }
+
+    public boolean isDeviceInteractive() {
+        return mKeyguardUpdateMonitor.isDeviceInteractive();
+    }
+
+    private void updateCanSkipBouncerState() {
+        mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser);
+    }
+
+    private void notifyKeyguardChanged() {
+        for (Callback callback : mCallbacks) {
+            callback.onKeyguardChanged();
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 1a9756f..a7fab41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -159,7 +159,7 @@
         mConfig = config;
         mReceiverHandler = new Handler(bgLooper);
         mCallbackHandler = callbackHandler;
-        mDataSaverController = new DataSaverController(context);
+        mDataSaverController = new DataSaverControllerImpl(context);
 
         mSubscriptionManager = subManager;
         mSubDefaults = defaultsHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
index 28935bf..e5b0c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
@@ -1,84 +1,24 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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.statusbar.policy;
 
 import android.app.AlarmManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
 
 import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-public class NextAlarmController extends BroadcastReceiver
-        implements CallbackController<NextAlarmChangeCallback> {
-
-    private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
-
-    private AlarmManager mAlarmManager;
-    private AlarmManager.AlarmClockInfo mNextAlarm;
-
-    public NextAlarmController(Context context) {
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
-        context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
-        updateNextAlarm();
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("NextAlarmController state:");
-        pw.print("  mNextAlarm="); pw.println(mNextAlarm);
-    }
-
-    public void addCallback(NextAlarmChangeCallback cb) {
-        mChangeCallbacks.add(cb);
-        cb.onNextAlarmChanged(mNextAlarm);
-    }
-
-    public void removeCallback(NextAlarmChangeCallback cb) {
-        mChangeCallbacks.remove(cb);
-    }
-
-    public void onReceive(Context context, Intent intent) {
-        final String action = intent.getAction();
-        if (action.equals(Intent.ACTION_USER_SWITCHED)
-                || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
-            updateNextAlarm();
-        }
-    }
-
-    private void updateNextAlarm() {
-        mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
-        fireNextAlarmChanged();
-    }
-
-    private void fireNextAlarmChanged() {
-        int n = mChangeCallbacks.size();
-        for (int i = 0; i < n; i++) {
-            mChangeCallbacks.get(i).onNextAlarmChanged(mNextAlarm);
-        }
-    }
+public interface NextAlarmController extends CallbackController<NextAlarmChangeCallback> {
 
     public interface NextAlarmChangeCallback {
         void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
new file mode 100644
index 0000000..dfdeae1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class NextAlarmControllerImpl extends BroadcastReceiver
+        implements NextAlarmController {
+
+    private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
+
+    private AlarmManager mAlarmManager;
+    private AlarmManager.AlarmClockInfo mNextAlarm;
+
+    public NextAlarmControllerImpl(Context context) {
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+        context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
+        updateNextAlarm();
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NextAlarmController state:");
+        pw.print("  mNextAlarm="); pw.println(mNextAlarm);
+    }
+
+    public void addCallback(NextAlarmChangeCallback cb) {
+        mChangeCallbacks.add(cb);
+        cb.onNextAlarmChanged(mNextAlarm);
+    }
+
+    public void removeCallback(NextAlarmChangeCallback cb) {
+        mChangeCallbacks.remove(cb);
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(Intent.ACTION_USER_SWITCHED)
+                || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+            updateNextAlarm();
+        }
+    }
+
+    private void updateNextAlarm() {
+        mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+        fireNextAlarmChanged();
+    }
+
+    private void fireNextAlarmChanged() {
+        int n = mChangeCallbacks.size();
+        for (int i = 0; i < n; i++) {
+            mChangeCallbacks.get(i).onNextAlarmChanged(mNextAlarm);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 69281b5..3142228 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -24,6 +24,7 @@
     String getDeviceOwnerName();
     String getProfileOwnerName();
     CharSequence getDeviceOwnerOrganizationName();
+    boolean isNetworkLoggingEnabled();
     boolean isVpnEnabled();
     boolean isVpnRestricted();
     /** Whether the VPN app should use branded VPN iconography.  */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 142f21b..df959bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -159,6 +159,11 @@
     }
 
     @Override
+    public boolean isNetworkLoggingEnabled() {
+        return mDevicePolicyManager.isNetworkLoggingEnabled(null);
+    }
+
+    @Override
     public boolean isVpnEnabled() {
         for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
             if (mCurrentVpns.get(profileId) != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index c09747b..1e23a20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -1,233 +1,28 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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.statusbar.policy;
 
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.ContactsContract;
-import android.util.Log;
 
-import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
 
-import java.util.ArrayList;
+public interface UserInfoController extends CallbackController<OnUserInfoChangedListener> {
 
-public class UserInfoController implements CallbackController<OnUserInfoChangedListener> {
-
-    private static final String TAG = "UserInfoController";
-
-    private final Context mContext;
-    private final ArrayList<OnUserInfoChangedListener> mCallbacks =
-            new ArrayList<OnUserInfoChangedListener>();
-    private AsyncTask<Void, Void, UserInfoQueryResult> mUserInfoTask;
-
-    private String mUserName;
-    private Drawable mUserDrawable;
-    private String mUserAccount;
-
-    public UserInfoController(Context context) {
-        mContext = context;
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mReceiver, filter);
-
-        IntentFilter profileFilter = new IntentFilter();
-        profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
-        profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
-        mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
-                null, null);
-    }
-
-    public void addCallback(OnUserInfoChangedListener callback) {
-        mCallbacks.add(callback);
-        callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
-    }
-
-    public void removeCallback(OnUserInfoChangedListener callback) {
-        mCallbacks.remove(callback);
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                reloadUserInfo();
-            }
-        }
-    };
-
-    private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
-                    Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
-                try {
-                    final int currentUser = ActivityManager.getService().getCurrentUser().id;
-                    final int changedUser =
-                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
-                    if (changedUser == currentUser) {
-                        reloadUserInfo();
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Couldn't get current user id for profile change", e);
-                }
-            }
-        }
-    };
-
-    public void reloadUserInfo() {
-        if (mUserInfoTask != null) {
-            mUserInfoTask.cancel(false);
-            mUserInfoTask = null;
-        }
-        queryForUserInformation();
-    }
-
-    private void queryForUserInformation() {
-        Context currentUserContext;
-        UserInfo userInfo;
-        try {
-            userInfo = ActivityManager.getService().getCurrentUser();
-            currentUserContext = mContext.createPackageContextAsUser("android", 0,
-                    new UserHandle(userInfo.id));
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Couldn't create user context", e);
-            throw new RuntimeException(e);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't get user info", e);
-            throw new RuntimeException(e);
-        }
-        final int userId = userInfo.id;
-        final boolean isGuest = userInfo.isGuest();
-        final String userName = userInfo.name;
-
-        final Resources res = mContext.getResources();
-        final int avatarSize = Math.max(
-                res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
-                res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
-
-        final Context context = currentUserContext;
-        mUserInfoTask = new AsyncTask<Void, Void, UserInfoQueryResult>() {
-
-            @Override
-            protected UserInfoQueryResult doInBackground(Void... params) {
-                final UserManager um = UserManager.get(mContext);
-
-                // Fall back to the UserManager nickname if we can't read the name from the local
-                // profile below.
-                String name = userName;
-                Drawable avatar = null;
-                Bitmap rawAvatar = um.getUserIcon(userId);
-                if (rawAvatar != null) {
-                    avatar = new UserIconDrawable(avatarSize)
-                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
-                } else {
-                    avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
-                            /* light= */ true);
-                }
-
-                // If it's a single-user device, get the profile name, since the nickname is not
-                // usually valid
-                if (um.getUsers().size() <= 1) {
-                    // Try and read the display name from the local profile
-                    final Cursor cursor = context.getContentResolver().query(
-                            ContactsContract.Profile.CONTENT_URI, new String[] {
-                                    ContactsContract.CommonDataKinds.Phone._ID,
-                                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
-                            }, null, null, null);
-                    if (cursor != null) {
-                        try {
-                            if (cursor.moveToFirst()) {
-                                name = cursor.getString(cursor.getColumnIndex(
-                                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
-                            }
-                        } finally {
-                            cursor.close();
-                        }
-                    }
-                }
-                String userAccount = um.getUserAccount(userId);
-                return new UserInfoQueryResult(name, avatar, userAccount);
-            }
-
-            @Override
-            protected void onPostExecute(UserInfoQueryResult result) {
-                mUserName = result.getName();
-                mUserDrawable = result.getAvatar();
-                mUserAccount = result.getUserAccount();
-                mUserInfoTask = null;
-                notifyChanged();
-            }
-        };
-        mUserInfoTask.execute();
-    }
-
-    private void notifyChanged() {
-        for (OnUserInfoChangedListener listener : mCallbacks) {
-            listener.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
-        }
-    }
-
-    public void onDensityOrFontScaleChanged() {
-        reloadUserInfo();
-    }
+    void reloadUserInfo();
 
     public interface OnUserInfoChangedListener {
         public void onUserInfoChanged(String name, Drawable picture, String userAccount);
     }
-
-    private static class UserInfoQueryResult {
-        private String mName;
-        private Drawable mAvatar;
-        private String mUserAccount;
-
-        public UserInfoQueryResult(String name, Drawable avatar, String userAccount) {
-            mName = name;
-            mAvatar = avatar;
-            mUserAccount = userAccount;
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        public Drawable getAvatar() {
-            return mAvatar;
-        }
-
-        public String getUserAccount() {
-            return mUserAccount;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
new file mode 100644
index 0000000..b1e4b03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.UserIconDrawable;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
+
+import java.util.ArrayList;
+
+public class UserInfoControllerImpl implements UserInfoController {
+
+    private static final String TAG = "UserInfoController";
+
+    private final Context mContext;
+    private final ArrayList<OnUserInfoChangedListener> mCallbacks =
+            new ArrayList<OnUserInfoChangedListener>();
+    private AsyncTask<Void, Void, UserInfoQueryResult> mUserInfoTask;
+
+    private String mUserName;
+    private Drawable mUserDrawable;
+    private String mUserAccount;
+
+    public UserInfoControllerImpl(Context context) {
+        mContext = context;
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiver(mReceiver, filter);
+
+        IntentFilter profileFilter = new IntentFilter();
+        profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
+        profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+        mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
+                null, null);
+    }
+
+    public void addCallback(OnUserInfoChangedListener callback) {
+        mCallbacks.add(callback);
+        callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
+    }
+
+    public void removeCallback(OnUserInfoChangedListener callback) {
+        mCallbacks.remove(callback);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                reloadUserInfo();
+            }
+        }
+    };
+
+    private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
+                    Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+                try {
+                    final int currentUser = ActivityManager.getService().getCurrentUser().id;
+                    final int changedUser =
+                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+                    if (changedUser == currentUser) {
+                        reloadUserInfo();
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Couldn't get current user id for profile change", e);
+                }
+            }
+        }
+    };
+
+    public void reloadUserInfo() {
+        if (mUserInfoTask != null) {
+            mUserInfoTask.cancel(false);
+            mUserInfoTask = null;
+        }
+        queryForUserInformation();
+    }
+
+    private void queryForUserInformation() {
+        Context currentUserContext;
+        UserInfo userInfo;
+        try {
+            userInfo = ActivityManager.getService().getCurrentUser();
+            currentUserContext = mContext.createPackageContextAsUser("android", 0,
+                    new UserHandle(userInfo.id));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Couldn't create user context", e);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't get user info", e);
+            throw new RuntimeException(e);
+        }
+        final int userId = userInfo.id;
+        final boolean isGuest = userInfo.isGuest();
+        final String userName = userInfo.name;
+
+        final Resources res = mContext.getResources();
+        final int avatarSize = Math.max(
+                res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
+                res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
+
+        final Context context = currentUserContext;
+        mUserInfoTask = new AsyncTask<Void, Void, UserInfoQueryResult>() {
+
+            @Override
+            protected UserInfoQueryResult doInBackground(Void... params) {
+                final UserManager um = UserManager.get(mContext);
+
+                // Fall back to the UserManager nickname if we can't read the name from the local
+                // profile below.
+                String name = userName;
+                Drawable avatar = null;
+                Bitmap rawAvatar = um.getUserIcon(userId);
+                if (rawAvatar != null) {
+                    avatar = new UserIconDrawable(avatarSize)
+                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
+                } else {
+                    avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
+                            /* light= */ true);
+                }
+
+                // If it's a single-user device, get the profile name, since the nickname is not
+                // usually valid
+                if (um.getUsers().size() <= 1) {
+                    // Try and read the display name from the local profile
+                    final Cursor cursor = context.getContentResolver().query(
+                            ContactsContract.Profile.CONTENT_URI, new String[] {
+                                    ContactsContract.CommonDataKinds.Phone._ID,
+                                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
+                            }, null, null, null);
+                    if (cursor != null) {
+                        try {
+                            if (cursor.moveToFirst()) {
+                                name = cursor.getString(cursor.getColumnIndex(
+                                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
+                            }
+                        } finally {
+                            cursor.close();
+                        }
+                    }
+                }
+                String userAccount = um.getUserAccount(userId);
+                return new UserInfoQueryResult(name, avatar, userAccount);
+            }
+
+            @Override
+            protected void onPostExecute(UserInfoQueryResult result) {
+                mUserName = result.getName();
+                mUserDrawable = result.getAvatar();
+                mUserAccount = result.getUserAccount();
+                mUserInfoTask = null;
+                notifyChanged();
+            }
+        };
+        mUserInfoTask.execute();
+    }
+
+    private void notifyChanged() {
+        for (OnUserInfoChangedListener listener : mCallbacks) {
+            listener.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount);
+        }
+    }
+
+    public void onDensityOrFontScaleChanged() {
+        reloadUserInfo();
+    }
+
+    private static class UserInfoQueryResult {
+        private String mName;
+        private Drawable mAvatar;
+        private String mUserAccount;
+
+        public UserInfoQueryResult(String name, Drawable avatar, String userAccount) {
+            mName = name;
+            mAvatar = avatar;
+            mUserAccount = userAccount;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public Drawable getAvatar() {
+            return mAvatar;
+        }
+
+        public String getUserAccount() {
+            return mUserAccount;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 26f74ea..e1ff297 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -22,6 +22,7 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
@@ -52,6 +53,11 @@
     private int mBaseZHeight;
     private int mMaxLayoutHeight;
     private ActivatableNotificationView mLastVisibleBackgroundChild;
+    private float mCurrentScrollVelocity;
+    private int mStatusBarState;
+    private float mExpandingVelocity;
+    private boolean mPanelTracking;
+    private boolean mExpansionChanging;
 
     public AmbientState(Context context) {
         reload(context);
@@ -241,4 +247,44 @@
     public ActivatableNotificationView getLastVisibleBackgroundChild() {
         return mLastVisibleBackgroundChild;
     }
+
+    public void setCurrentScrollVelocity(float currentScrollVelocity) {
+        mCurrentScrollVelocity = currentScrollVelocity;
+    }
+
+    public float getCurrentScrollVelocity() {
+        return mCurrentScrollVelocity;
+    }
+
+    public boolean isOnKeyguard() {
+        return mStatusBarState == StatusBarState.KEYGUARD;
+    }
+
+    public void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+    }
+
+    public void setExpandingVelocity(float expandingVelocity) {
+        mExpandingVelocity = expandingVelocity;
+    }
+
+    public void setExpansionChanging(boolean expansionChanging) {
+        mExpansionChanging = expansionChanging;
+    }
+
+    public boolean isExpansionChanging() {
+        return mExpansionChanging;
+    }
+
+    public float getExpandingVelocity() {
+        return mExpandingVelocity;
+    }
+
+    public void setPanelTracking(boolean panelTracking) {
+        mPanelTracking = panelTracking;
+    }
+
+    public boolean isPanelTracking() {
+        return mPanelTracking;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index d3d58f9..38bb40e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -120,7 +120,7 @@
         }
     }
 
-    private void combineFilter(AnimationFilter filter) {
+    public void combineFilter(AnimationFilter filter) {
         animateAlpha |= filter.animateAlpha;
         animateX |= filter.animateX;
         animateY |= filter.animateY;
@@ -134,7 +134,7 @@
         hasDelays |= filter.hasDelays;
     }
 
-    private void reset() {
+    public void reset() {
         animateAlpha = false;
         animateX = false;
         animateY = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index b8f8cb2..22709f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -213,7 +213,7 @@
         mDividers.add(newIndex, divider);
 
         updateGroupOverflow();
-        row.setIconTransformationAmount(0, false /* isLastChild */);
+        row.setContentTransformationAmount(0, false /* isLastChild */);
     }
 
     public void removeNotification(ExpandableNotificationRow row) {
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 10d995c..7d4927b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -357,6 +357,7 @@
     private Rect mRequestedClipBounds;
     private boolean mInHeadsUpPinnedMode;
     private boolean mHeadsUpAnimatingAway;
+    private int mStatusBarState;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -575,6 +576,9 @@
      */
     private void updateChildren() {
         updateScrollStateForAddedChildren();
+        mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
+                ? 0
+                : mScroller.getCurrVelocity());
         mAmbientState.setScrollY(mOwnScrollY);
         mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
         if (!isCurrentlyAnimating() && !mNeedsAnimation) {
@@ -715,7 +719,6 @@
             requestChildrenUpdate();
         }
         setStackTranslation(translationY);
-        requestChildrenUpdate();
     }
 
     private void setRequestedClipBounds(Rect clipRect) {
@@ -1185,7 +1188,7 @@
     }
 
     private boolean onKeyguard() {
-        return mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD;
+        return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
     private void setSwipingInProgress(boolean isSwiped) {
@@ -2122,7 +2125,7 @@
             top = mTopPadding;
             bottom = top;
         }
-        if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+        if (mStatusBarState != StatusBarState.KEYGUARD) {
             top = (int) Math.max(mTopPadding + mStackTranslation, top);
         } else {
             // otherwise the animation from the shade to the keyguard will jump as it's maxed
@@ -2356,7 +2359,7 @@
                 }
                 break;
             case MotionEvent.ACTION_UP:
-                if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD && mTouchIsClick &&
+                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
                         isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
                     mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
                 }
@@ -3121,10 +3124,12 @@
 
     public void onExpansionStarted() {
         mIsExpansionChanging = true;
+        mAmbientState.setExpansionChanging(true);
     }
 
     public void onExpansionStopped() {
         mIsExpansionChanging = false;
+        mAmbientState.setExpansionChanging(false);
         if (!mIsExpanded) {
             setOwnScrollY(0);
             mPhoneStatusBar.resetUserExpandedStates();
@@ -3152,9 +3157,11 @@
 
     public void onPanelTrackingStarted() {
         mPanelTracking = true;
+        mAmbientState.setPanelTracking(true);
     }
     public void onPanelTrackingStopped() {
         mPanelTracking = false;
+        mAmbientState.setPanelTracking(false);
     }
 
     public void resetScrollPosition() {
@@ -3979,6 +3986,15 @@
         updateClipping();
     }
 
+    public void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+        mAmbientState.setStatusBarState(statusBarState);
+    }
+
+    public void setExpandingVelocity(float expandingVelocity) {
+        mAmbientState.setExpandingVelocity(expandingVelocity);
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 7afc7ba..50b6d70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -124,7 +124,8 @@
 
     private void updateClipping(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
-        float drawStart = ambientState.getTopPadding() + ambientState.getStackTranslation();
+        float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
+                + ambientState.getStackTranslation() : 0;
         float previousNotificationEnd = 0;
         float previousNotificationStart = 0;
         int childCount = algorithmState.visibleChildren.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 8a5ddd4..b747592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -99,36 +99,6 @@
             // don't do anything with it
             return;
         }
-        boolean becomesInvisible = this.alpha == 0.0f || this.hidden;
-        boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
-        if (animatingAlpha) {
-            updateAlphaAnimation(view);
-        } else if (view.getAlpha() != this.alpha) {
-            // apply layer type
-            boolean becomesFullyVisible = this.alpha == 1.0f;
-            boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
-                    && view.hasOverlappingRendering();
-            int layerType = view.getLayerType();
-            int newLayerType = newLayerTypeIsHardware
-                    ? View.LAYER_TYPE_HARDWARE
-                    : View.LAYER_TYPE_NONE;
-            if (layerType != newLayerType) {
-                view.setLayerType(newLayerType, null);
-            }
-
-            // apply alpha
-            view.setAlpha(this.alpha);
-        }
-
-        // apply visibility
-        int oldVisibility = view.getVisibility();
-        int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
-        if (newVisibility != oldVisibility) {
-            if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
-                // We don't want views to change visibility when they are animating to GONE
-                view.setVisibility(newVisibility);
-            }
-        }
 
         // apply xTranslation
         boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X);
@@ -163,6 +133,53 @@
         if (view.getScaleY() != this.scaleY) {
             view.setScaleY(this.scaleY);
         }
+
+        boolean becomesInvisible = this.alpha == 0.0f || (this.hidden && !isAnimating(view));
+        boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
+        if (animatingAlpha) {
+            updateAlphaAnimation(view);
+        } else if (view.getAlpha() != this.alpha) {
+            // apply layer type
+            boolean becomesFullyVisible = this.alpha == 1.0f;
+            boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
+                    && view.hasOverlappingRendering();
+            int layerType = view.getLayerType();
+            int newLayerType = newLayerTypeIsHardware
+                    ? View.LAYER_TYPE_HARDWARE
+                    : View.LAYER_TYPE_NONE;
+            if (layerType != newLayerType) {
+                view.setLayerType(newLayerType, null);
+            }
+
+            // apply alpha
+            view.setAlpha(this.alpha);
+        }
+
+        // apply visibility
+        int oldVisibility = view.getVisibility();
+        int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
+        if (newVisibility != oldVisibility) {
+            if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
+                // We don't want views to change visibility when they are animating to GONE
+                view.setVisibility(newVisibility);
+            }
+        }
+    }
+
+    protected boolean isAnimating(View view) {
+        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) {
+            return true;
+        }
+        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) {
+            return true;
+        }
+        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) {
+            return true;
+        }
+        if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
+            return true;
+        }
+        return false;
     }
 
     private boolean isAnimating(View view, int tag) {
@@ -482,7 +499,7 @@
                 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
                 child.setTag(TAG_START_TRANSLATION_Y, null);
                 child.setTag(TAG_END_TRANSLATION_Y, null);
-                onYTranslationAnimationFinished();
+                onYTranslationAnimationFinished(child);
             }
         });
         startAnimator(animator, listener);
@@ -491,7 +508,10 @@
         child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
     }
 
-    protected void onYTranslationAnimationFinished() {
+    protected void onYTranslationAnimationFinished(View view) {
+        if (hidden) {
+            view.setVisibility(View.INVISIBLE);
+        }
     }
 
     protected void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
@@ -548,7 +568,26 @@
         }
     }
 
-    public static boolean isAnimatingY(ExpandableView child) {
+    public static boolean isAnimatingY(View child) {
         return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
     }
+
+    public void cancelAnimations(View view) {
+        Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_ALPHA);
+        if (animator != null) {
+            animator.cancel();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index ebc962d..565ac08 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -92,6 +92,10 @@
         mUserTracker.startTracking();
     }
 
+    public void destroy() {
+        mUserTracker.stopTracking();
+    }
+
     private void upgradeTuner(int oldVersion, int newVersion) {
         if (oldVersion < 1) {
             String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
diff --git a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
new file mode 100644
index 0000000..f420921
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Builder class to create a {@link LayoutInflater} with various properties.
+ *
+ * Call any desired configuration methods on the Builder and then use
+ * {@link Builder#build} to create the LayoutInflater. This is an alternative to directly using
+ * {@link LayoutInflater#setFilter} and {@link LayoutInflater#setFactory}.
+ * @hide for use by framework
+ */
+public class LayoutInflaterBuilder {
+    private static final String TAG = "LayoutInflaterBuilder";
+
+    private Context mFromContext;
+    private Context mTargetContext;
+    private Map<String, String> mReplaceMap;
+    private Set<Class> mDisallowedClasses;
+    private LayoutInflater mBuiltInflater;
+
+    /**
+     * Creates a new Builder which will construct a LayoutInflater.
+     *
+     * @param fromContext This context's LayoutInflater will be cloned by the Builder using
+     * {@link LayoutInflater#cloneInContext}. By default, the new LayoutInflater will point at
+     * this same Context.
+     */
+    public LayoutInflaterBuilder(@NonNull Context fromContext) {
+        mFromContext = fromContext;
+        mTargetContext = fromContext;
+        mReplaceMap = null;
+        mDisallowedClasses = null;
+        mBuiltInflater = null;
+    }
+
+    /**
+     * Instructs the Builder to point the LayoutInflater at a different Context.
+     *
+     * @param targetContext Context to be provided to
+     * {@link LayoutInflater#cloneInContext(Context)}.
+     * @return Builder object post-modification.
+     */
+    public LayoutInflaterBuilder target(@NonNull Context targetContext) {
+        assertIfAlreadyBuilt();
+        mTargetContext = targetContext;
+        return this;
+    }
+
+    /**
+     * Instructs the Builder to configure the LayoutInflater such that all instances
+     * of one {@link View} will be replaced with instances of another during inflation.
+     *
+     * @param from Instances of this class will be replaced during inflation.
+     * @param to Instances of this class will be inflated as replacements.
+     * @return Builder object post-modification.
+     */
+    public LayoutInflaterBuilder replace(@NonNull Class from, @NonNull Class to) {
+        assertIfAlreadyBuilt();
+        if (mReplaceMap == null) {
+            mReplaceMap = new ArrayMap<String, String>();
+        }
+        mReplaceMap.put(from.getName(), to.getName());
+        return this;
+    }
+
+    /**
+     * Instructs the Builder to configure the LayoutInflater such that any attempt to inflate
+     * a {@link View} of a given type will throw a {@link InflateException}.
+     *
+     * @param disallowedClass The Class type that will be disallowed.
+     * @return Builder object post-modification.
+     */
+    public LayoutInflaterBuilder disallow(@NonNull Class disallowedClass) {
+        assertIfAlreadyBuilt();
+        if (mDisallowedClasses == null) {
+            mDisallowedClasses = new ArraySet<Class>();
+        }
+        mDisallowedClasses.add(disallowedClass);
+        return this;
+    }
+
+    /**
+     * Builds and returns the LayoutInflater.  Afterwards, this Builder can no longer can be
+     * used, all future calls on the Builder will throw {@link AssertionError}.
+     */
+    public LayoutInflater build() {
+        assertIfAlreadyBuilt();
+        mBuiltInflater =
+                LayoutInflater.from(mFromContext).cloneInContext(mTargetContext);
+        setFactoryIfNeeded(mBuiltInflater);
+        setFilterIfNeeded(mBuiltInflater);
+        return mBuiltInflater;
+    }
+
+    private void assertIfAlreadyBuilt() {
+        if (mBuiltInflater != null) {
+            throw new AssertionError("Cannot use this Builder after build() has been called.");
+        }
+    }
+
+    private void setFactoryIfNeeded(LayoutInflater inflater) {
+        if (mReplaceMap == null) {
+            return;
+        }
+        inflater.setFactory(
+                new LayoutInflater.Factory() {
+                    @Override
+                    public View onCreateView(String name, Context context, AttributeSet attrs) {
+                        String replacingClassName = mReplaceMap.get(name);
+                        if (replacingClassName != null) {
+                            try {
+                                return inflater.createView(replacingClassName, null, attrs);
+                            } catch (ClassNotFoundException e) {
+                                Log.e(TAG, "Could not replace " + name
+                                        + " with " + replacingClassName
+                                        + ", Exception: ", e);
+                            }
+                        }
+                        return null;
+                    }
+                });
+    }
+
+    private void setFilterIfNeeded(LayoutInflater inflater) {
+        if (mDisallowedClasses == null) {
+            return;
+        }
+        inflater.setFilter(
+                new LayoutInflater.Filter() {
+                    @Override
+                    public boolean onLoadClass(Class clazz) {
+                        return !mDisallowedClasses.contains(clazz);
+                    }
+                });
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
index f87336c..447edac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
@@ -26,6 +26,8 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -139,7 +141,7 @@
 
     private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
         public HostCallbacks() {
-            super(getTrackedContext(), FragmentTestCase.this.mHandler, 0);
+            super(mContext, FragmentTestCase.this.mHandler, 0);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java
deleted file mode 100644
index d64669d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/LeakCheckedTest.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.CallbackController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Base class for tests to check if receivers are left registered, services bound, or other
- * listeners listening.
- */
-public class LeakCheckedTest extends SysuiTestCase {
-    private static final String TAG = "LeakCheckedTest";
-
-    private final Map<String, Tracker> mTrackers = new HashMap<>();
-    private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
-    private TrackingContext mTrackedContext;
-
-    @Rule
-    public TestWatcher successWatcher = new TestWatcher() {
-        @Override
-        protected void succeeded(Description description) {
-            verify();
-        }
-    };
-
-    @Before
-    public void setup() {
-        mTrackedContext = new TrackingContext(mContext);
-        addSupportedLeakCheckers();
-    }
-
-    public <T> T getLeakChecker(Class<T> cls) {
-        T obj = (T) mLeakCheckers.get(cls);
-        if (obj == null) {
-            Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
-        }
-        return obj;
-    }
-
-    public Context getTrackedContext() {
-        return mTrackedContext;
-    }
-
-    private Tracker getTracker(String tag) {
-        Tracker t = mTrackers.get(tag);
-        if (t == null) {
-            t = new Tracker();
-            mTrackers.put(tag, t);
-        }
-        return t;
-    }
-
-    public void verify() {
-        mTrackers.values().forEach(Tracker::verify);
-    }
-
-    public static class Tracker {
-        private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
-
-        LeakInfo getLeakInfo(Object object) {
-            LeakInfo leakInfo = mObjects.get(object);
-            if (leakInfo == null) {
-                leakInfo = new LeakInfo();
-                mObjects.put(object, leakInfo);
-            }
-            return leakInfo;
-        }
-
-        private void verify() {
-            mObjects.values().forEach(LeakInfo::verify);
-        }
-    }
-
-    public static class LeakInfo {
-        private List<Throwable> mThrowables = new ArrayList<>();
-
-        private LeakInfo() {
-        }
-
-        private void addAllocation(Throwable t) {
-            // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
-            mThrowables.add(t);
-        }
-
-        private void clearAllocations() {
-            mThrowables.clear();
-        }
-
-        public void verify() {
-            if (mThrowables.size() == 0) return;
-            Log.e(TAG, "Listener or binding not properly released");
-            for (Throwable t : mThrowables) {
-                Log.e(TAG, "Allocation found", t);
-            }
-            StringWriter writer = new StringWriter();
-            mThrowables.get(0).printStackTrace(new PrintWriter(writer));
-            Assert.fail("Listener or binding not properly released\n"
-                    + writer.toString());
-        }
-    }
-
-    private void addSupportedLeakCheckers() {
-        addListening("bluetooth", BluetoothController.class);
-        addListening("location", LocationController.class);
-        addListening("rotation", RotationLockController.class);
-        addListening("zen", ZenModeController.class);
-        addListening("cast", CastController.class);
-        addListening("hotspot", HotspotController.class);
-        addListening("flashlight", FlashlightController.class);
-        addListening("user", UserInfoController.class);
-        addListening("keyguard", KeyguardMonitor.class);
-        addListening("battery", BatteryController.class);
-        addListening("security", SecurityController.class);
-        addListening("profile", ManagedProfileController.class);
-        addListening("alarm", NextAlarmController.class);
-        NetworkController network = addListening("network", NetworkController.class);
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker("emergency").getLeakInfo(invocation.getArguments()[0])
-                        .addAllocation(new Throwable());
-                return null;
-            }
-        }).when(network).addEmergencyListener(any());
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker("emergency").getLeakInfo(invocation.getArguments()[0]).clearAllocations();
-                return null;
-            }
-        }).when(network).removeEmergencyListener(any());
-        DataSaverController datasaver = addListening("datasaver", DataSaverController.class);
-        when(network.getDataSaverController()).thenReturn(datasaver);
-    }
-
-    private <T extends CallbackController> T addListening(final String tag, Class<T> cls) {
-        T mock = mock(cls);
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker(tag).getLeakInfo(invocation.getArguments()[0])
-                        .addAllocation(new Throwable());
-                return null;
-            }
-        }).when(mock).addCallback(any());
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
-                return null;
-            }
-        }).when(mock).removeCallback(any());
-        mLeakCheckers.put(cls, mock);
-        return mock;
-    }
-
-    class TrackingContext extends ContextWrapper {
-        public TrackingContext(Context base) {
-            super(base);
-        }
-
-        @Override
-        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-            getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
-            return super.registerReceiver(receiver, filter);
-        }
-
-        @Override
-        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
-                String broadcastPermission, Handler scheduler) {
-            getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
-            return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
-        }
-
-        @Override
-        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
-                IntentFilter filter, String broadcastPermission, Handler scheduler) {
-            getTracker("receiver").getLeakInfo(receiver).addAllocation(new Throwable());
-            return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
-                    scheduler);
-        }
-
-        @Override
-        public void unregisterReceiver(BroadcastReceiver receiver) {
-            getTracker("receiver").getLeakInfo(receiver).clearAllocations();
-            super.unregisterReceiver(receiver);
-        }
-
-        @Override
-        public boolean bindService(Intent service, ServiceConnection conn, int flags) {
-            getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
-            return super.bindService(service, conn, flags);
-        }
-
-        @Override
-        public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
-                Handler handler, UserHandle user) {
-            getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
-            return super.bindServiceAsUser(service, conn, flags, handler, user);
-        }
-
-        @Override
-        public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
-                UserHandle user) {
-            getTracker("service").getLeakInfo(conn).addAllocation(new Throwable());
-            return super.bindServiceAsUser(service, conn, flags, user);
-        }
-
-        @Override
-        public void unbindService(ServiceConnection conn) {
-            getTracker("service").getLeakInfo(conn).clearAllocations();
-            super.unbindService(conn);
-        }
-
-        @Override
-        public void registerComponentCallbacks(ComponentCallbacks callback) {
-            getTracker("component").getLeakInfo(callback).addAllocation(new Throwable());
-            super.registerComponentCallbacks(callback);
-        }
-
-        @Override
-        public void unregisterComponentCallbacks(ComponentCallbacks callback) {
-            getTracker("component").getLeakInfo(callback).clearAllocations();
-            super.unregisterComponentCallbacks(callback);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 5dac8e5..008580a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -22,6 +22,7 @@
 import android.os.MessageQueue;
 
 import com.android.systemui.utils.TestableContext;
+import com.android.systemui.utils.leaks.Tracker;
 
 import org.junit.After;
 import org.junit.Before;
@@ -29,14 +30,14 @@
 /**
  * Base class that does System UI specific setup.
  */
-public class SysuiTestCase {
+public abstract class SysuiTestCase {
 
     private Handler mHandler;
     protected TestableContext mContext;
 
     @Before
     public void SysuiSetup() throws Exception {
-        mContext = new TestableContext(InstrumentationRegistry.getTargetContext());
+        mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
     }
 
     @After
@@ -71,6 +72,11 @@
         }
     }
 
+    // Used for leak tracking, returns null to indicate no leak tracking by default.
+    public Tracker getTracker(String tag) {
+        return null;
+    }
+
     public static final class EmptyRunnable implements Runnable {
         public void run() {
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
index 1987009..4c25c62e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
@@ -35,9 +35,11 @@
 
 import static junit.framework.Assert.assertEquals;
 import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -53,6 +55,8 @@
 
     private ViewGroup mRootView = mock(ViewGroup.class);
     private TextView mFooterText = mock(TextView.class);
+    private ImageView mFooterIcon = mock(ImageView.class);
+    private ImageView mFooterIcon2 = mock(ImageView.class);
     private QSFooter mFooter;
     private Resources mResources;
     private SecurityController mSecurityController = mock(SecurityController.class);
@@ -60,7 +64,8 @@
     @Before
     public void setUp() {
         when(mRootView.findViewById(R.id.footer_text)).thenReturn(mFooterText);
-        when(mRootView.findViewById(R.id.footer_icon)).thenReturn(mock(ImageView.class));
+        when(mRootView.findViewById(R.id.footer_icon)).thenReturn(mFooterIcon);
+        when(mRootView.findViewById(R.id.footer_icon2)).thenReturn(mFooterIcon2);
         final LayoutInflater layoutInflater = mock(LayoutInflater.class);
         when(layoutInflater.inflate(eq(R.layout.quick_settings_footer), anyObject(), anyBoolean()))
                 .thenReturn(mRootView);
@@ -114,6 +119,48 @@
     }
 
     @Test
+    public void testNetworkLoggingEnabled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(true);
+        when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
+        when(mSecurityController.isVpnEnabled()).thenReturn(false);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        verify(mFooterIcon).setVisibility(View.VISIBLE);
+        verify(mFooterIcon).setImageResource(R.drawable.ic_qs_network_logging);
+        verify(mFooterIcon2).setVisibility(View.INVISIBLE);
+    }
+
+    @Test
+    public void testVpnEnabled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(true);
+        when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(false);
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.isVpnBranded()).thenReturn(false);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        verify(mFooterIcon).setVisibility(View.VISIBLE);
+        verify(mFooterIcon, never()).setImageResource(anyInt());
+        verify(mFooterIcon2).setVisibility(View.INVISIBLE);
+    }
+
+    @Test
+    public void testNetworkLoggingAndVpnEnabled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(true);
+        when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.isVpnBranded()).thenReturn(false);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        verify(mFooterIcon).setVisibility(View.VISIBLE);
+        verify(mFooterIcon, never()).setImageResource(anyInt());
+        verify(mFooterIcon2).setVisibility(View.VISIBLE);
+        verify(mFooterIcon2, never()).setImageResource(anyInt());
+    }
+
+    @Test
     public void testGetMessageWithNoOrganizationAndNoVPN() {
         assertEquals(getExpectedMessage(false /* hasDeviceOwnerOrganization */, false /* hasVPN */),
                 mFooter.getMessage(DEVICE_OWNER_PACKAGE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 6ceaead..350a95f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -21,8 +21,10 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.systemui.FragmentTestCase;
+import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.QuickStatusBarHeader;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
@@ -38,6 +40,7 @@
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.tuner.TunerService;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -59,7 +62,7 @@
         KeyguardMonitor keyguardMonitor = getLeakChecker(KeyguardMonitor.class);
         when(userSwitcher.getKeyguardMonitor()).thenReturn(keyguardMonitor);
         when(userSwitcher.getUsers()).thenReturn(new ArrayList<>());
-        QSTileHost host = new QSTileHost(getTrackedContext(),
+        QSTileHost host = new QSTileHost(mContext,
                 mock(PhoneStatusBar.class),
                 getLeakChecker(BluetoothController.class),
                 getLeakChecker(LocationController.class),
@@ -85,6 +88,12 @@
         qs.setListening(false);
         waitForIdleSync(h);
 
+        // Manually push header through detach so it can handle standard cleanup it does on
+        // removed from window.
+        ((QuickStatusBarHeader) qs.getView().findViewById(R.id.header)).onDetachedFromWindow();
+
         host.destroy();
+        // Ensure the tuner cleans up its persistent listeners.
+        TunerService.get(mContext).destroy();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
index 5179823..bf73416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
@@ -14,18 +14,31 @@
 
 package com.android.systemui.utils;
 
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks;
 import android.content.ContentProviderClient;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.Settings;
 
+import com.android.systemui.utils.leaks.Tracker;
+import com.android.systemui.SysuiTestCase;
+
 public class TestableContext extends ContextWrapper {
 
     private final FakeContentResolver mFakeContentResolver;
     private final FakeSettingsProvider mSettingsProvider;
 
-    public TestableContext(Context base) {
+    private Tracker mReceiver;
+    private Tracker mService;
+    private Tracker mComponent;
+
+    public TestableContext(Context base, SysuiTestCase test) {
         super(base);
         mFakeContentResolver = new FakeContentResolver(base);
         ContentProviderClient settings = base.getContentResolver()
@@ -33,6 +46,9 @@
         mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings,
                 mFakeContentResolver);
         mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
+        mReceiver = test.getTracker("receiver");
+        mService = test.getTracker("service");
+        mComponent = test.getTracker("component");
     }
 
     public FakeSettingsProvider getSettingsProvider() {
@@ -49,4 +65,69 @@
         // Return this so its always a TestableContext.
         return this;
     }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
+        return super.registerReceiver(receiver, filter);
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
+        return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+    }
+
+    @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler) {
+        if (mReceiver != null) mReceiver.getLeakInfo(receiver).addAllocation(new Throwable());
+        return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+                scheduler);
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        if (mReceiver != null) mReceiver.getLeakInfo(receiver).clearAllocations();
+        super.unregisterReceiver(receiver);
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+        if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+        return super.bindService(service, conn, flags);
+    }
+
+    @Override
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            Handler handler, UserHandle user) {
+        if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+        return super.bindServiceAsUser(service, conn, flags, handler, user);
+    }
+
+    @Override
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            UserHandle user) {
+        if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
+        return super.bindServiceAsUser(service, conn, flags, user);
+    }
+
+    @Override
+    public void unbindService(ServiceConnection conn) {
+        if (mService != null) mService.getLeakInfo(conn).clearAllocations();
+        super.unbindService(conn);
+    }
+
+    @Override
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
+        super.registerComponentCallbacks(callback);
+    }
+
+    @Override
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        if (mComponent != null) mComponent.getLeakInfo(callback).clearAllocations();
+        super.unregisterComponentCallbacks(callback);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
new file mode 100644
index 0000000..0238bf7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.CallbackController;
+
+public class BaseLeakChecker<T> implements CallbackController<T> {
+
+    private final Tracker mTracker;
+
+    public BaseLeakChecker(LeakCheckedTest test, String tag) {
+        mTracker = test.getTracker(tag);
+    }
+
+    protected final Tracker getTracker() {
+        return mTracker;
+    }
+
+    @Override
+    public void addCallback(T listener) {
+        mTracker.getLeakInfo(listener).addAllocation(new Throwable());
+    }
+
+    @Override
+    public void removeCallback(T listener) {
+        mTracker.getLeakInfo(listener).clearAllocations();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
new file mode 100644
index 0000000..fa07d33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import android.os.Bundle;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
+        implements BatteryController {
+    public FakeBatteryController(LeakCheckedTest test) {
+        super(test, "battery");
+    }
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+
+    }
+
+    @Override
+    public void setPowerSaveMode(boolean powerSave) {
+
+    }
+
+    @Override
+    public boolean isPowerSave() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
new file mode 100644
index 0000000..6074a01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothController.Callback;
+
+import java.util.Collection;
+
+public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
+        BluetoothController {
+
+    public FakeBluetoothController(LeakCheckedTest test) {
+        super(test, "bluetooth");
+    }
+
+    @Override
+    public boolean isBluetoothSupported() {
+        return false;
+    }
+
+    @Override
+    public boolean isBluetoothEnabled() {
+        return false;
+    }
+
+    @Override
+    public int getBluetoothState() {
+        return 0;
+    }
+
+    @Override
+    public boolean isBluetoothConnected() {
+        return false;
+    }
+
+    @Override
+    public boolean isBluetoothConnecting() {
+        return false;
+    }
+
+    @Override
+    public String getLastDeviceName() {
+        return null;
+    }
+
+    @Override
+    public void setBluetoothEnabled(boolean enabled) {
+
+    }
+
+    @Override
+    public Collection<CachedBluetoothDevice> getDevices() {
+        return null;
+    }
+
+    @Override
+    public void connect(CachedBluetoothDevice device) {
+
+    }
+
+    @Override
+    public void disconnect(CachedBluetoothDevice device) {
+
+    }
+
+    @Override
+    public boolean canConfigBluetooth() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
new file mode 100644
index 0000000..08211f8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastController.Callback;
+
+import java.util.Set;
+
+public class FakeCastController extends BaseLeakChecker<Callback> implements CastController {
+    public FakeCastController(LeakCheckedTest test) {
+        super(test, "cast");
+    }
+
+    @Override
+    public void setDiscovering(boolean request) {
+
+    }
+
+    @Override
+    public void setCurrentUserId(int currentUserId) {
+
+    }
+
+    @Override
+    public Set<CastDevice> getCastDevices() {
+        return null;
+    }
+
+    @Override
+    public void startCasting(CastDevice device) {
+
+    }
+
+    @Override
+    public void stopCasting(CastDevice device) {
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
new file mode 100644
index 0000000..857a785
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+
+public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController {
+
+    public FakeDataSaverController(LeakCheckedTest test) {
+        super(test, "datasaver");
+    }
+
+    @Override
+    public boolean isDataSaverEnabled() {
+        return false;
+    }
+
+    @Override
+    public void setDataSaverEnabled(boolean enabled) {
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
new file mode 100644
index 0000000..630abd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+
+public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
+        implements FlashlightController {
+    public FakeFlashlightController(LeakCheckedTest test) {
+        super(test, "flashlight");
+    }
+
+    @Override
+    public boolean hasFlashlight() {
+        return false;
+    }
+
+    @Override
+    public void setFlashlight(boolean newState) {
+
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return false;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
new file mode 100644
index 0000000..781960d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotController.Callback;
+
+public class FakeHotspotController extends BaseLeakChecker<Callback> implements HotspotController {
+
+    public FakeHotspotController(LeakCheckedTest test) {
+        super(test, "hotspot");
+    }
+
+    @Override
+    public boolean isHotspotEnabled() {
+        return false;
+    }
+
+    @Override
+    public void setHotspotEnabled(boolean enabled) {
+
+    }
+
+    @Override
+    public boolean isHotspotSupported() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
new file mode 100644
index 0000000..39bbf2d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+
+public class FakeKeyguardMonitor implements KeyguardMonitor {
+
+    private final BaseLeakChecker<Callback> mCallbackController;
+
+    public FakeKeyguardMonitor(LeakCheckedTest test) {
+        mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
+    }
+
+    @Override
+    public void addCallback(Callback callback) {
+        mCallbackController.addCallback(callback);
+    }
+
+    @Override
+    public void removeCallback(Callback callback) {
+        mCallbackController.removeCallback(callback);
+    }
+
+    @Override
+    public boolean isSecure() {
+        return false;
+    }
+
+    @Override
+    public boolean isShowing() {
+        return false;
+    }
+
+    @Override
+    public boolean canSkipBouncer() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
new file mode 100644
index 0000000..eab436c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
+
+public class FakeLocationController extends BaseLeakChecker<LocationSettingsChangeCallback>
+        implements LocationController {
+    public FakeLocationController(LeakCheckedTest test) {
+        super(test, "location");
+    }
+
+    @Override
+    public boolean isLocationEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean setLocationEnabled(boolean enabled) {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
new file mode 100644
index 0000000..0ec0d77
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
+
+public class FakeManagedProfileController extends BaseLeakChecker<Callback> implements
+        ManagedProfileController {
+    public FakeManagedProfileController(LeakCheckedTest test) {
+        super(test, "profile");
+    }
+
+    @Override
+    public void setWorkModeEnabled(boolean enabled) {
+
+    }
+
+    @Override
+    public boolean hasActiveProfile() {
+        return false;
+    }
+
+    @Override
+    public boolean isWorkModeEnabled() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
new file mode 100644
index 0000000..fcfe9aa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+
+public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
+        implements NetworkController {
+
+    private final FakeDataSaverController mDataSaverController;
+    private final BaseLeakChecker<EmergencyListener> mEmergencyChecker;
+
+    public FakeNetworkController(LeakCheckedTest test) {
+        super(test, "network");
+        mDataSaverController = new FakeDataSaverController(test);
+        mEmergencyChecker = new BaseLeakChecker<EmergencyListener>(test, "emergency");
+    }
+
+    @Override
+    public void addEmergencyListener(EmergencyListener listener) {
+        mEmergencyChecker.addCallback(listener);
+    }
+
+    @Override
+    public void removeEmergencyListener(EmergencyListener listener) {
+        mEmergencyChecker.removeCallback(listener);
+    }
+
+    @Override
+    public DataSaverController getDataSaverController() {
+        return mDataSaverController;
+    }
+
+    @Override
+    public boolean hasMobileDataFeature() {
+        return false;
+    }
+
+    @Override
+    public void setWifiEnabled(boolean enabled) {
+
+    }
+
+    @Override
+    public void onUserSwitched(int newUserId) {
+
+    }
+
+    @Override
+    public AccessPointController getAccessPointController() {
+        return null;
+    }
+
+    @Override
+    public DataUsageController getMobileDataController() {
+        return null;
+    }
+
+    @Override
+    public boolean hasVoiceCallingFeature() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
new file mode 100644
index 0000000..707fc4b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+
+public class FakeNextAlarmController extends BaseLeakChecker<NextAlarmChangeCallback>
+        implements NextAlarmController {
+
+    public FakeNextAlarmController(LeakCheckedTest test) {
+        super(test, "alarm");
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
new file mode 100644
index 0000000..00e2404
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+
+public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback>
+        implements RotationLockController {
+    public FakeRotationLockController(LeakCheckedTest test) {
+        super(test, "rotation");
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+
+    }
+
+    @Override
+    public int getRotationLockOrientation() {
+        return 0;
+    }
+
+    @Override
+    public boolean isRotationLockAffordanceVisible() {
+        return false;
+    }
+
+    @Override
+    public boolean isRotationLocked() {
+        return false;
+    }
+
+    @Override
+    public void setRotationLocked(boolean locked) {
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
new file mode 100644
index 0000000..2d53c77
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
+
+public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCallback>
+        implements SecurityController {
+    public FakeSecurityController(LeakCheckedTest test) {
+        super(test, "security");
+    }
+
+    @Override
+    public boolean isDeviceManaged() {
+        return false;
+    }
+
+    @Override
+    public boolean hasProfileOwner() {
+        return false;
+    }
+
+    @Override
+    public String getDeviceOwnerName() {
+        return null;
+    }
+
+    @Override
+    public String getProfileOwnerName() {
+        return null;
+    }
+
+    @Override
+    public CharSequence getDeviceOwnerOrganizationName() {
+        return null;
+    }
+
+    @Override
+    public boolean isNetworkLoggingEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isVpnEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isVpnRestricted() {
+        return false;
+    }
+
+    @Override
+    public boolean isVpnBranded() {
+        return false;
+    }
+
+    @Override
+    public String getPrimaryVpnName() {
+        return null;
+    }
+
+    @Override
+    public String getProfileVpnName() {
+        return null;
+    }
+
+    @Override
+    public void onUserSwitched(int newUserId) {
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
new file mode 100644
index 0000000..578b310
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
+
+public class FakeUserInfoController extends BaseLeakChecker<OnUserInfoChangedListener>
+        implements UserInfoController {
+    public FakeUserInfoController(LeakCheckedTest test) {
+        super(test, "user_info");
+    }
+
+    @Override
+    public void reloadUserInfo() {
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
new file mode 100644
index 0000000..13ea385
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ZenRule;
+
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeController.Callback;
+
+public class FakeZenModeController extends BaseLeakChecker<Callback> implements ZenModeController {
+    public FakeZenModeController(LeakCheckedTest test) {
+        super(test, "zen");
+    }
+
+    @Override
+    public void setZen(int zen, Uri conditionId, String reason) {
+
+    }
+
+    @Override
+    public int getZen() {
+        return 0;
+    }
+
+    @Override
+    public ZenRule getManualRule() {
+        return null;
+    }
+
+    @Override
+    public ZenModeConfig getConfig() {
+        return null;
+    }
+
+    @Override
+    public long getNextAlarm() {
+        return 0;
+    }
+
+    @Override
+    public void setUserId(int userId) {
+
+    }
+
+    @Override
+    public boolean isZenAvailable() {
+        return false;
+    }
+
+    @Override
+    public ComponentName getEffectsSuppressor() {
+        return null;
+    }
+
+    @Override
+    public boolean isCountdownConditionSupported() {
+        return false;
+    }
+
+    @Override
+    public int getCurrentUser() {
+        return 0;
+    }
+
+    @Override
+    public boolean isVolumeRestricted() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
new file mode 100644
index 0000000..728ed60
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+
+import android.util.ArrayMap;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class for tests to check if receivers are left registered, services bound, or other
+ * listeners listening.
+ */
+public abstract class LeakCheckedTest extends SysuiTestCase {
+    private static final String TAG = "LeakCheckedTest";
+
+    private final Map<String, Tracker> mTrackers = new HashMap<>();
+    private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
+
+    @Rule
+    public TestWatcher successWatcher = new TestWatcher() {
+        @Override
+        protected void succeeded(Description description) {
+            verify();
+        }
+    };
+
+    public <T> T getLeakChecker(Class<T> cls) {
+        Object obj = mLeakCheckers.get(cls);
+        if (obj == null) {
+            // Lazy create checkers so we only have the ones we need.
+            if (cls == BluetoothController.class) {
+                obj = new FakeBluetoothController(this);
+            } else if (cls == LocationController.class) {
+                obj = new FakeLocationController(this);
+            } else if (cls == RotationLockController.class) {
+                obj = new FakeRotationLockController(this);
+            } else if (cls == ZenModeController.class) {
+                obj = new FakeZenModeController(this);
+            } else if (cls == CastController.class) {
+                obj = new FakeCastController(this);
+            } else if (cls == HotspotController.class) {
+                obj = new FakeHotspotController(this);
+            } else if (cls == FlashlightController.class) {
+                obj = new FakeFlashlightController(this);
+            } else if (cls == UserInfoController.class) {
+                obj = new FakeUserInfoController(this);
+            } else if (cls == KeyguardMonitor.class) {
+                obj = new FakeKeyguardMonitor(this);
+            } else if (cls == BatteryController.class) {
+                obj = new FakeBatteryController(this);
+            } else if (cls == SecurityController.class) {
+                obj = new FakeSecurityController(this);
+            } else if (cls == ManagedProfileController.class) {
+                obj = new FakeManagedProfileController(this);
+            } else if (cls == NextAlarmController.class) {
+                obj = new FakeNextAlarmController(this);
+            } else if (cls == NetworkController.class) {
+                obj = new FakeNetworkController(this);
+            } else {
+                Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
+            }
+            mLeakCheckers.put(cls, obj);
+        }
+        return (T) obj;
+    }
+
+    @Override
+    public Tracker getTracker(String tag) {
+        Tracker t = mTrackers.get(tag);
+        if (t == null) {
+            t = new Tracker();
+            mTrackers.put(tag, t);
+        }
+        return t;
+    }
+
+    public void verify() {
+        mTrackers.values().forEach(Tracker::verify);
+    }
+
+    public <T extends CallbackController> T addListening(T mock, Class<T> cls, String tag) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                getTracker(tag).getLeakInfo(invocation.getArguments()[0])
+                        .addAllocation(new Throwable());
+                return null;
+            }
+        }).when(mock).addCallback(any());
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
+                return null;
+            }
+        }).when(mock).removeCallback(any());
+        mLeakCheckers.put(cls, mock);
+        return mock;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
new file mode 100644
index 0000000..1d016fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class LeakInfo {
+    private static final String TAG = "LeakInfo";
+    private List<Throwable> mThrowables = new ArrayList<>();
+
+    LeakInfo() {
+    }
+
+    public void addAllocation(Throwable t) {
+        // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
+        mThrowables.add(t);
+    }
+
+    public void clearAllocations() {
+        mThrowables.clear();
+    }
+
+    void verify() {
+        if (mThrowables.size() == 0) return;
+        Log.e(TAG, "Listener or binding not properly released");
+        for (Throwable t : mThrowables) {
+            Log.e(TAG, "Allocation found", t);
+        }
+        StringWriter writer = new StringWriter();
+        mThrowables.get(0).printStackTrace(new PrintWriter(writer));
+        Assert.fail("Listener or binding not properly released\n"
+                + writer.toString());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
new file mode 100644
index 0000000..26ffd10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.utils.leaks;
+
+import android.util.ArrayMap;
+
+import com.android.systemui.utils.leaks.LeakInfo;
+
+import java.util.Map;
+
+public class Tracker {
+    private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
+
+    public LeakInfo getLeakInfo(Object object) {
+        LeakInfo leakInfo = mObjects.get(object);
+        if (leakInfo == null) {
+            leakInfo = new LeakInfo();
+            mObjects.put(object, leakInfo);
+        }
+        return leakInfo;
+    }
+
+    void verify() {
+        mObjects.values().forEach(LeakInfo::verify);
+    }
+}
diff --git a/preloaded-classes b/preloaded-classes
index a79ae50..da3861a 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -365,6 +365,8 @@
 android.app.BackStackRecord
 android.app.BackStackRecord$Op
 android.app.BackStackRecord$TransitionState
+android.app.ContentProviderHolder
+android.app.ContentProviderHolder$1
 android.app.ContextImpl
 android.app.ContextImpl$ApplicationContentResolver
 android.app.Dialog
@@ -383,8 +385,8 @@
 android.app.FragmentManagerImpl$1
 android.app.FragmentTransaction
 android.app.IActivityManager
-android.app.IActivityManager$ContentProviderHolder
-android.app.IActivityManager$ContentProviderHolder$1
+android.app.IActivityManager$Stub
+android.app.IActivityManager$Stub$Proxy
 android.app.IAlarmManager
 android.app.IAlarmManager$Stub
 android.app.IAlarmManager$Stub$Proxy
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 29b318f..cf372bc 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -17,6 +17,22 @@
   optional int32 network_id = 1;
 };
 
+// Transport describes a physical technology used by a network. It is a subset
+// of the TRANSPORT_* constants defined in android.net.NetworkCapabilities.
+enum Transport {
+  UNKNOWN   = 0;
+  BLUETOOTH = 1;
+  CELLULAR  = 2;
+  ETHERNET  = 3;
+  WIFI      = 4;
+};
+
+// A pair of (key, value) integers for describing histogram-like statistics.
+message Pair {
+  optional int32 key = 1;
+  optional int32 value = 2;
+};
+
 // Logs changes in the system default network. Changes can be 1) acquiring a
 // default network with no previous default, 2) a switch of the system default
 // network to a new default network, 3) a loss of the system default network.
@@ -49,7 +65,8 @@
 // This message is associated to android.net.metrics.IpReachabilityEvent.
 message IpReachabilityEvent {
   // The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
-  optional string if_name = 1;
+  // Deprecated since version 2, replaced by transport field.
+  optional string if_name = 1 [deprecated = true];
 
   // The event type code of the probe, represented by constants defined in
   // android.net.metrics.IpReachabilityEvent.
@@ -93,6 +110,7 @@
 
 // Logs DNS lookup latencies. Repeated fields must have the same length.
 // This message is associated to android.net.metrics.DnsEvent.
+// Deprecated since version 2.
 message DNSLookupBatch {
   // The id of the network on which the DNS lookups took place.
   optional NetworkId network_id = 1;
@@ -107,13 +125,62 @@
   repeated int32 latencies_ms = 4;
 };
 
+// Represents a collections of DNS lookup latencies and counters for a
+// particular combination of DNS query type and return code.
+// Since version 2.
+message DNSLatencies {
+  // The type of the DNS lookups, as defined in android.net.metrics.DnsEvent.
+  // Acts as a key for a set of DNS query results.
+  // Possible values are: 0 for getaddrinfo, 1 for gethostbyname.
+  optional int32 type = 1;
+
+  // The return value of the DNS resolver for the DNS lookups.
+  // Acts as a key for a set of DNS query results.
+  // Possible values are: 0 for success, or errno code for failures.
+  optional int32 return_code = 2;
+
+  // The number of query operations recorded.
+  optional int32 query_count = 3;
+
+  // The number of query operations returning A IPv4 records.
+  optional int32 a_count = 4;
+
+  // The number of query operations returning AAAA IPv6 records.
+  optional int32 aaaa_count = 5;
+
+  // The time it took for each DNS lookup to complete. The number of repeated
+  // values can be less than query_count in case of event rate-limiting.
+  repeated int32 latencies_ms = 6;
+};
+
+// Represents latency and errno statistics of the connect() system call.
+// Since version 2.
+message ConnectStatistics {
+  // The number of connect() operations recorded.
+  optional int32 connect_count = 1;
+
+  // The number of connect() operations with IPv6 socket address.
+  optional int32 ipv6_addr_count = 2;
+
+  // The time it took for each successful connect() operation to complete.
+  // The number of repeated values can be less than connect_count in case of
+  // event rate-limiting.
+  repeated int32 latencies_ms = 3;
+
+  // Counts of all error values returned by failed connect() operations.
+  // The Pair key field is the errno code. The Pair value field is the count
+  // for that errno code.
+  repeated Pair errnos_counters = 4;
+};
+
 // Represents a DHCP event on a single interface, which can be a DHCPClient
 // state transition or a response packet parsing error.
 // This message is associated to android.net.metrics.DhcpClientEvent and
 // android.net.metrics.DhcpErrorEvent.
 message DHCPEvent {
   // The interface name (wlan, rmnet, lo, ...) on which the event happened.
-  optional string if_name = 1;
+  // Deprecated since version 2, replaced by transport field.
+  optional string if_name = 1 [deprecated = true];
 
   oneof value {
     // The name of a state in the DhcpClient state machine, represented by
@@ -217,7 +284,8 @@
 // This message is associated to android.net.metrics.IpManagerEvent.
 message IpProvisioningEvent {
   // The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
-  optional string if_name = 1;
+  // Deprecated since version 2, replaced by transport field.
+  optional string if_name = 1 [deprecated = true];
 
   // The code of the IP provisioning event, represented by constants defined in
   // android.net.metrics.IpManagerEvent.
@@ -228,11 +296,15 @@
 }
 
 // Represents one of the IP connectivity event defined in this file.
-// Next tag: 12
+// Next tag: 15
 message IpConnectivityEvent {
   // Time in ms when the event was recorded.
   optional int64 time_ms = 1;
 
+  // Physical transport of the network on which the event happened.
+  // Since version 2.
+  optional Transport transport = 12;
+
   // Event type.
   oneof event {
 
@@ -246,7 +318,14 @@
     NetworkEvent network_event = 4;
 
     // A batch of DNS lookups.
-    DNSLookupBatch dns_lookup_batch = 5;
+    // Deprecated in the nyc-mr2 release since version 2, and replaced by dns_latencies.
+    DNSLookupBatch dns_lookup_batch = 5 [deprecated = true];
+
+    // DNS lookup latency statistics.
+    DNSLatencies dns_latencies = 13;
+
+    // Connect latency and errno statistics.
+    ConnectStatistics connect_statistics = 14;
 
     // A DHCP client event or DHCP receive error.
     DHCPEvent dhcp_event = 6;
@@ -277,9 +356,9 @@
   optional int32 dropped_events = 2;
 
   // The version number of the metrics events being collected.
-  //  nyc-dev: not populated, implicitly 0
-  //  nyc-dr1: not populated, implicitly 1 (sailfish and marlin only)
-  //  nyc-mr1: not populated, implicitly 1
-  //  nyc-mr2: 2
+  //  nyc-dev: not populated, implicitly 0.
+  //  nyc-dr1: not populated, implicitly 1 (sailfish and marlin only).
+  //  nyc-mr1: not populated, implicitly 1.
+  //  nyc-mr2: 2.
   optional int32 version = 3;
 };
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c1bb506..ef9d8f2 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3089,6 +3089,22 @@
     // ACTION: Logged when a provisioning session has completed
     PROVISIONING_SESSION_COMPLETED = 735;
 
+    // ACTION: An app requested the permission READ_PHONE_NUMBER
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_PERMISSION_REQUEST_READ_PHONE_NUMBER = 736;
+
+    // ACTION: An app was granted the permission READ_PHONE_NUMBER
+    // PACKAGE: The package name of the app that was granted the permission
+    ACTION_PERMISSION_GRANT_READ_PHONE_NUMBER = 737;
+
+    // ACTION: An app requested the permission READ_PHONE_NUMBER and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_PERMISSION_DENIED_READ_PHONE_NUMBER = 738;
+
+    // ACTION: The permission READ_PHONE_NUMBER was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBER = 739;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5eceb9f..c9fd568 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4606,28 +4606,9 @@
         } catch (Exception e) {
             loge("Exception in setDnsConfigurationForNetwork: " + e);
         }
-        final NetworkAgentInfo defaultNai = getDefaultNetwork();
-        if (defaultNai != null && defaultNai.network.netId == netId) {
-            setDefaultDnsSystemProperties(dnses);
-        }
         flushVmDnsCache();
     }
 
-    private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
-        int last = 0;
-        for (InetAddress dns : dnses) {
-            ++last;
-            String key = "net.dns" + last;
-            String value = dns.getHostAddress();
-            SystemProperties.set(key, value);
-        }
-        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
-            String key = "net.dns" + i;
-            SystemProperties.set(key, "");
-        }
-        mNumDnsEntries = last;
-    }
-
     private String getNetworkPermission(NetworkCapabilities nc) {
         // TODO: make these permission strings AIDL constants instead.
         if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
@@ -4844,7 +4825,6 @@
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
-        setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 8092b4a..9f63e30 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -211,7 +211,10 @@
     private long mMaintenanceStartTime;
 
     private int mActiveIdleOpCount;
-    private PowerManager.WakeLock mActiveIdleWakeLock;
+    private PowerManager.WakeLock mActiveIdleWakeLock; // held when there are operations in progress
+    private PowerManager.WakeLock mGoingIdleWakeLock;  // held when we are going idle so hardware
+                                                       // (especially NetworkPolicyManager) can shut
+                                                       // down.
     private boolean mJobsActive;
     private boolean mAlarmsActive;
     private boolean mReportedMaintenanceActivity;
@@ -998,14 +1001,14 @@
         }
     }
 
-    static final int MSG_WRITE_CONFIG = 1;
-    static final int MSG_REPORT_IDLE_ON = 2;
-    static final int MSG_REPORT_IDLE_ON_LIGHT = 3;
-    static final int MSG_REPORT_IDLE_OFF = 4;
-    static final int MSG_REPORT_ACTIVE = 5;
-    static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
-    static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
-    static final int MSG_FINISH_IDLE_OP = 8;
+    private static final int MSG_WRITE_CONFIG = 1;
+    private static final int MSG_REPORT_IDLE_ON = 2;
+    private static final int MSG_REPORT_IDLE_ON_LIGHT = 3;
+    private static final int MSG_REPORT_IDLE_OFF = 4;
+    private static final int MSG_REPORT_ACTIVE = 5;
+    private static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+    private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
+    private static final int MSG_FINISH_IDLE_OP = 8;
 
     final class MyHandler extends Handler {
         MyHandler(Looper looper) {
@@ -1016,10 +1019,12 @@
             if (DEBUG) Slog.d(TAG, "handleMessage(" + msg.what + ")");
             switch (msg.what) {
                 case MSG_WRITE_CONFIG: {
+                    // Does not hold a wakelock. Just let this happen whenever.
                     handleWriteConfigFile();
                 } break;
                 case MSG_REPORT_IDLE_ON:
                 case MSG_REPORT_IDLE_ON_LIGHT: {
+                    // mGoingIdleWakeLock is held at this point
                     EventLogTags.writeDeviceIdleOnStart();
                     final boolean deepChanged;
                     final boolean lightChanged;
@@ -1044,8 +1049,10 @@
                         getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
                     }
                     EventLogTags.writeDeviceIdleOnComplete();
+                    mGoingIdleWakeLock.release();
                 } break;
                 case MSG_REPORT_IDLE_OFF: {
+                    // mActiveIdleWakeLock is held at this point
                     EventLogTags.writeDeviceIdleOffStart("unknown");
                     final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                     final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
@@ -1071,6 +1078,7 @@
                     EventLogTags.writeDeviceIdleOffComplete();
                 } break;
                 case MSG_REPORT_ACTIVE: {
+                    // The device is awake at this point, so no wakelock necessary.
                     String activeReason = (String)msg.obj;
                     int activeUid = msg.arg1;
                     EventLogTags.writeDeviceIdleOffStart(
@@ -1092,10 +1100,12 @@
                     EventLogTags.writeDeviceIdleOffComplete();
                 } break;
                 case MSG_TEMP_APP_WHITELIST_TIMEOUT: {
+                    // TODO: What is keeping the device awake at this point? Does it need to be?
                     int uid = msg.arg1;
                     checkTempAppWhitelistTimeout(uid);
                 } break;
                 case MSG_REPORT_MAINTENANCE_ACTIVITY: {
+                    // TODO: What is keeping the device awake at this point? Does it need to be?
                     boolean active = (msg.arg1 == 1);
                     final int size = mMaintenanceActivityListeners.beginBroadcast();
                     try {
@@ -1111,6 +1121,7 @@
                     }
                 } break;
                 case MSG_FINISH_IDLE_OP: {
+                    // mActiveIdleWakeLock is held at this point
                     decActiveIdleOps();
                 } break;
             }
@@ -1356,6 +1367,9 @@
                 mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                         "deviceidle_maint");
                 mActiveIdleWakeLock.setReferenceCounted(false);
+                mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                        "deviceidle_going_idle");
+                mGoingIdleWakeLock.setReferenceCounted(true);
                 mConnectivityService = (ConnectivityService)ServiceManager.getService(
                         Context.CONNECTIVITY_SERVICE);
                 mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
@@ -1898,6 +1912,7 @@
                 mLightState = LIGHT_STATE_IDLE;
                 EventLogTags.writeDeviceIdleLight(mLightState, reason);
                 addEvent(EVENT_LIGHT_IDLE);
+                mGoingIdleWakeLock.acquire();
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
                 break;
             case LIGHT_STATE_IDLE:
@@ -2023,6 +2038,7 @@
                 }
                 EventLogTags.writeDeviceIdle(mState, reason);
                 addEvent(EVENT_DEEP_IDLE);
+                mGoingIdleWakeLock.acquire();
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
                 break;
             case STATE_IDLE:
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 3dfbdc3..372e9a6 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1105,7 +1105,6 @@
                     mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
                             mHardKeyboardListener);
                 }
-                buildInputMethodListLocked(!mImeSelectedOnBoot /* resetDefaultEnabledIme */);
                 if (!mImeSelectedOnBoot) {
                     Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
                     resetStateIfCurrentLocaleChangedLocked();
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 4c9ea58..27e4aa4 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -36,10 +36,12 @@
 import android.net.wifi.WifiConfiguration;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.R;
@@ -52,11 +54,11 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Backing service for {@link android.net.NetworkScoreManager}.
@@ -68,7 +70,8 @@
 
     private final Context mContext;
     private final NetworkScorerAppManager mNetworkScorerAppManager;
-    private final Map<Integer, INetworkScoreCache> mScoreCaches;
+    @GuardedBy("mScoreCaches")
+    private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
     /** Lock used to update mPackageMonitor when scorer package changes occur. */
     private final Object mPackageMonitorLock = new Object[0];
 
@@ -166,7 +169,7 @@
     NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
         mContext = context;
         mNetworkScorerAppManager = networkScoreAppManager;
-        mScoreCaches = new HashMap<>();
+        mScoreCaches = new ArrayMap<>();
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
         // TODO: Need to update when we support per-user scorers. http://b/23422763
         mContext.registerReceiverAsUser(
@@ -276,7 +279,7 @@
         }
 
         // Separate networks by type.
-        Map<Integer, List<ScoredNetwork>> networksByType = new HashMap<>();
+        Map<Integer, List<ScoredNetwork>> networksByType = new ArrayMap<>();
         for (ScoredNetwork network : networks) {
             List<ScoredNetwork> networkList = networksByType.get(network.networkKey.type);
             if (networkList == null) {
@@ -287,19 +290,32 @@
         }
 
         // Pass the scores of each type down to the appropriate network scorer.
-        for (Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
-            INetworkScoreCache scoreCache = mScoreCaches.get(entry.getKey());
-            if (scoreCache != null) {
-                try {
-                    scoreCache.updateScores(entry.getValue());
-                } catch (RemoteException e) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
+        for (final Map.Entry<Integer, List<ScoredNetwork>> entry : networksByType.entrySet()) {
+            final RemoteCallbackList<INetworkScoreCache> callbackList;
+            final boolean isEmpty;
+            synchronized (mScoreCaches) {
+                callbackList = mScoreCaches.get(entry.getKey());
+                isEmpty = callbackList == null || callbackList.getRegisteredCallbackCount() == 0;
+            }
+            if (isEmpty) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
+                }
+                continue;
+            }
+
+            sendCallback(new Consumer<INetworkScoreCache>() {
+                @Override
+                public void accept(INetworkScoreCache networkScoreCache) {
+                    try {
+                        networkScoreCache.updateScores(entry.getValue());
+                    } catch (RemoteException e) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
+                        }
                     }
                 }
-            } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "No scorer registered for type " + entry.getKey() + ", discarding");
-            }
+            }, Collections.singleton(callbackList));
         }
 
         return true;
@@ -394,28 +410,54 @@
 
     /** Clear scores. Callers are responsible for checking permissions as appropriate. */
     private void clearInternal() {
-        Set<INetworkScoreCache> cachesToClear = getScoreCaches();
+        sendCallback(new Consumer<INetworkScoreCache>() {
+            @Override
+            public void accept(INetworkScoreCache networkScoreCache) {
+                try {
+                    networkScoreCache.clearScores();
+                } catch (RemoteException e) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "Unable to clear scores", e);
+                    }
+                }
+            }
+        }, getScoreCacheLists());
+    }
 
-        for (INetworkScoreCache scoreCache : cachesToClear) {
-            try {
-                scoreCache.clearScores();
-            } catch (RemoteException e) {
+    @Override
+    public void registerNetworkScoreCache(int networkType,
+                                          INetworkScoreCache scoreCache,
+                                          int filterType) {
+        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+        synchronized (mScoreCaches) {
+            RemoteCallbackList<INetworkScoreCache> callbackList = mScoreCaches.get(networkType);
+            if (callbackList == null) {
+                callbackList = new RemoteCallbackList<>();
+                mScoreCaches.put(networkType, callbackList);
+            }
+            if (!callbackList.register(scoreCache, filterType)) {
+                if (callbackList.getRegisteredCallbackCount() == 0) {
+                    mScoreCaches.remove(networkType);
+                }
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "Unable to clear scores", e);
+                    Log.v(TAG, "Unable to register NetworkScoreCache for type " + networkType);
                 }
             }
         }
     }
 
     @Override
-    public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
+    public void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
         mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
         synchronized (mScoreCaches) {
-            if (mScoreCaches.containsKey(networkType)) {
-                throw new IllegalArgumentException(
-                        "Score cache already registered for type " + networkType);
+            RemoteCallbackList<INetworkScoreCache> callbackList = mScoreCaches.get(networkType);
+            if (callbackList == null || !callbackList.unregister(scoreCache)) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Unable to unregister NetworkScoreCache for type " + networkType);
+                }
+            } else if (callbackList.getRegisteredCallbackCount() == 0) {
+                mScoreCaches.remove(networkType);
             }
-            mScoreCaches.put(networkType, scoreCache);
         }
     }
 
@@ -430,7 +472,7 @@
     }
 
     @Override
-    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+    protected void dump(final FileDescriptor fd, final PrintWriter writer, final String[] args) {
         mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
         NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
         if (currentScorer == null) {
@@ -439,13 +481,17 @@
         }
         writer.println("Current scorer: " + currentScorer.mPackageName);
 
-        for (INetworkScoreCache scoreCache : getScoreCaches()) {
-            try {
-                TransferPipe.dumpAsync(scoreCache.asBinder(), fd, args);
-            } catch (IOException | RemoteException e) {
-                writer.println("Failed to dump score cache: " + e);
+        sendCallback(new Consumer<INetworkScoreCache>() {
+            @Override
+            public void accept(INetworkScoreCache networkScoreCache) {
+                try {
+                  TransferPipe.dumpAsync(networkScoreCache.asBinder(), fd, args);
+                } catch (IOException | RemoteException e) {
+                  writer.println("Failed to dump score cache: " + e);
+                }
             }
-        }
+        }, getScoreCacheLists());
+
         if (mServiceConnection != null) {
             mServiceConnection.dump(fd, writer, args);
         } else {
@@ -455,14 +501,30 @@
     }
 
     /**
-     * Returns a set of all score caches that are currently active.
+     * Returns a {@link Collection} of all {@link RemoteCallbackList}s that are currently active.
      *
      * <p>May be used to perform an action on all score caches without potentially strange behavior
      * if a new scorer is registered during that action's execution.
      */
-    private Set<INetworkScoreCache> getScoreCaches() {
+    private Collection<RemoteCallbackList<INetworkScoreCache>> getScoreCacheLists() {
         synchronized (mScoreCaches) {
-            return new HashSet<>(mScoreCaches.values());
+            return new ArrayList<>(mScoreCaches.values());
+        }
+    }
+
+    private void sendCallback(Consumer<INetworkScoreCache> consumer,
+            Collection<RemoteCallbackList<INetworkScoreCache>> remoteCallbackLists) {
+        for (RemoteCallbackList<INetworkScoreCache> callbackList : remoteCallbackLists) {
+            synchronized (callbackList) { // Ensure only one active broadcast per RemoteCallbackList
+                final int count = callbackList.beginBroadcast();
+                try {
+                    for (int i = 0; i < count; i++) {
+                        consumer.accept(callbackList.getBroadcastItem(i));
+                    }
+                } finally {
+                    callbackList.finishBroadcast();
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 73aca65..8a0d4df 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -31,6 +31,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 
 import libcore.io.IoUtils;
 
@@ -85,6 +86,8 @@
 
     private int mAllowedUid = -1;
     private long mBlockDeviceSize;
+
+    @GuardedBy("mLock")
     private boolean mIsWritable = true;
 
     public PersistentDataBlockService(Context context) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 8fd1d2f..c65aed7 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2136,14 +2136,17 @@
             try {
                 accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    accounts.accountsDb.deleteDeAccount(accountId);
-                    if (userUnlocked) {
-                        // Delete from CE table
-                        accounts.accountsDb.deleteCeAccount(accountId);
-                    }
-                    accounts.accountsDb.setTransactionSuccessful();
-                    isChanged = true;
+                    isChanged = accounts.accountsDb.deleteDeAccount(accountId);
                 }
+                // always delete from CE table if CE storage is available
+                // DE account could be removed while CE was locked
+                if (userUnlocked) {
+                    long ceAccountId = accounts.accountsDb.findCeAccountId(account);
+                    if (ceAccountId >= 0) {
+                        accounts.accountsDb.deleteCeAccount(ceAccountId);
+                    }
+                }
+                accounts.accountsDb.setTransactionSuccessful();
             } finally {
                 accounts.accountsDb.endTransaction();
             }
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index a160b3a..5ca74711 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -1201,7 +1201,7 @@
     }
 
     boolean deleteCeAccount(long accountId) {
-        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
         return db.delete(
                 CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a5b3020..3990a8b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -43,7 +43,6 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -62,6 +61,7 @@
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.WindowManagerService;
@@ -269,10 +269,8 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -6205,8 +6203,19 @@
     }
 
     private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
-        ProcessRecord old = mProcessNames.remove(name, uid);
-        if (old != null) {
+        return removeProcessNameLocked(name, uid, null);
+    }
+
+    private final ProcessRecord removeProcessNameLocked(final String name, final int uid,
+            final ProcessRecord expecting) {
+        ProcessRecord old = mProcessNames.get(name, uid);
+        // Only actually remove when the currently recorded value matches the
+        // record that we expected; if it doesn't match then we raced with a
+        // newly created process and we don't want to destroy the new one.
+        if ((expecting == null) || (old == expecting)) {
+            mProcessNames.remove(name, uid);
+        }
+        if (old != null && old.uidRecord != null) {
             old.uidRecord.numProcs--;
             if (old.uidRecord.numProcs == 0) {
                 // No more processes using this uid, tell clients it is gone.
@@ -7496,45 +7505,51 @@
 
     @Override
     public void enterPictureInPictureMode(IBinder token) {
-        enterPictureInPictureMode(token, DEFAULT_DISPLAY, null /* aspectRatio */);
+        enterPictureInPictureMode(token, DEFAULT_DISPLAY, -1f /* aspectRatio */,
+                false /* checkAspectRatio */);
     }
 
     @Override
     public void enterPictureInPictureModeWithAspectRatio(IBinder token, float aspectRatio) {
-        enterPictureInPictureMode(token, DEFAULT_DISPLAY, aspectRatio);
+        enterPictureInPictureMode(token, DEFAULT_DISPLAY, aspectRatio, true /* checkAspectRatio */);
     }
 
-    public void enterPictureInPictureMode(IBinder token, int displayId, Float aspectRatio) {
+    private void enterPictureInPictureMode(IBinder token, int displayId, float aspectRatio,
+            boolean checkAspectRatio) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(this) {
-                if (!mSupportsPictureInPicture) {
-                    throw new IllegalStateException("enterPictureInPictureMode: "
-                            + "Device doesn't support picture-in-picture mode.");
-                }
+                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
+                        "enterPictureInPictureMode", token, aspectRatio, checkAspectRatio,
+                        true /* checkActivityVisibility */);
 
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null) {
-                    throw new IllegalStateException("enterPictureInPictureMode: "
-                            + "Can't find activity for token=" + token);
-                }
+                enterPictureInPictureModeLocked(r, displayId, aspectRatio,
+                        true /* moveHomeStackToFront */, "enterPictureInPictureMode");
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
 
-                if (!r.supportsPictureInPicture()) {
-                    throw new IllegalArgumentException("enterPictureInPictureMode: "
-                            + "Picture-In-Picture not supported for r=" + r);
-                }
+    void enterPictureInPictureModeLocked(ActivityRecord r, int displayId, float aspectRatio,
+            boolean moveHomeStackToFront, String reason) {
+        final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio)
+                ? mWindowManager.getPictureInPictureBounds(displayId, aspectRatio)
+                : mWindowManager.getPictureInPictureDefaultBounds(displayId);
+        mStackSupervisor.moveActivityToPinnedStackLocked(r, reason, bounds, moveHomeStackToFront);
+    }
 
-                if (aspectRatio != null && !isValidPictureInPictureAspectRatio(aspectRatio)) {
-                    throw new IllegalArgumentException(String.format("enterPictureInPictureMode: "
-                            + "Aspect ratio is too extreme (must be between %f and %f).",
-                                    mMinPipAspectRatio, mMaxPipAspectRatio));
-                }
+    @Override
+    public void enterPictureInPictureModeOnMoveToBackground(IBinder token,
+            boolean enterPictureInPictureOnMoveToBg) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized(this) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
+                        "requestAutoEnterPictureInPicture", token, -1f /* aspectRatio */,
+                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
 
-                final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio)
-                        ? mWindowManager.getPictureInPictureBounds(displayId, aspectRatio)
-                        : mWindowManager.getPictureInPictureDefaultBounds(displayId);
-                mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
-                        bounds);
+                r.supportsPipOnMoveToBackground = enterPictureInPictureOnMoveToBg;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -7546,33 +7561,68 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(this) {
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null || r.getStack().mStackId != PINNED_STACK_ID) {
-                    throw new IllegalStateException("setPictureInPictureAspectRatio: "
-                            + "Requesting activity must be in picture-in-picture mode.");
-                }
+                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
+                        "setPictureInPictureAspectRatio", token, aspectRatio,
+                        true /* checkAspectRatio */, false /* checkActivityVisibility */);
 
-                if (!isValidPictureInPictureAspectRatio(aspectRatio)) {
-                    throw new IllegalArgumentException(String.format(
-                            "setPictureInPictureAspectRatio: Aspect ratio is too extreme (must be "
-                                    + "between %f and %f).", mMinPipAspectRatio,
-                            mMaxPipAspectRatio));
+                if (r.getStack().getStackId() == PINNED_STACK_ID) {
+                    // If the activity is already in picture-in-picture, update the pinned stack now
+                    mWindowManager.setPictureInPictureAspectRatio(aspectRatio);
                 }
-
-                mWindowManager.setPictureInPictureAspectRatio(aspectRatio);
+                r.pictureInPictureAspectRatio = aspectRatio;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
-    private boolean isValidPictureInPictureAspectRatio(Float aspectRatio) {
-        if (aspectRatio == null) {
-            return false;
-        }
+    private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
         return mMinPipAspectRatio <= aspectRatio && aspectRatio <= mMaxPipAspectRatio;
     }
 
+    /**
+     * Checks the state of the system and the activity associated with the given {@param token} to
+     * verify that picture-in-picture is supported for that activity.
+     *
+     * @param checkAspectRatio whether or not to check {@param aspectRatio} is within a valid range
+     * @param checkActivityVisibility whether or not to enforce that the activity is currently
+     *                                visible
+     *
+     * @return the activity record for the given {@param token} if all the checks pass.
+     */
+    private ActivityRecord ensureValidPictureInPictureActivityLocked(String caller, IBinder token,
+            float aspectRatio, boolean checkAspectRatio, boolean checkActivityVisibility) {
+        if (!mSupportsPictureInPicture) {
+            throw new IllegalStateException(caller
+                    + ": Device doesn't support picture-in-picture mode.");
+        }
+
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            throw new IllegalStateException(caller
+                    + ": Can't find activity for token=" + token);
+        }
+
+        if (!r.canEnterPictureInPicture(checkActivityVisibility)) {
+            throw new IllegalArgumentException(caller
+                    + "Current activity does not support picture-in-picture or is not "
+                    + "visible r=" + r);
+        }
+
+        if (r.getStack().isHomeStack()) {
+            throw new IllegalStateException(caller
+                    + ": Activities on the home stack not supported");
+        }
+
+        if (checkAspectRatio && !isValidPictureInPictureAspectRatio(aspectRatio)) {
+            throw new IllegalArgumentException(String.format(caller
+                    + ": Aspect ratio is too extreme (must be between %f and %f).",
+                            mMinPipAspectRatio, mMaxPipAspectRatio));
+        }
+
+        return r;
+    }
+
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -17002,7 +17052,7 @@
             if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                     "Removing non-persistent process during cleanup: " + app);
             if (!replacingPid) {
-                removeProcessNameLocked(app.processName, app.uid);
+                removeProcessNameLocked(app.processName, app.uid, app);
             }
             if (mHeavyWeightProcess == app) {
                 mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d2a560f..13c422b 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -21,7 +21,6 @@
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
@@ -84,7 +83,6 @@
 import android.util.TimeUtils;
 import android.view.AppTransitionAnimationSpec;
 import android.view.IApplicationToken;
-import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.app.ResolverActivity;
@@ -218,6 +216,14 @@
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
     boolean immersive;      // immersive mode (don't interrupt if possible)
     boolean forceNewConfig; // force re-create with new config next time
+    boolean supportsPipOnMoveToBackground;   // Supports automatically entering picture-in-picture
+            // when this activity is hidden. This flag is requested by the activity.
+    private boolean enterPipOnMoveToBackground; // Flag to enter picture in picture when this
+            // activity is made invisible. This flag is set specifically when another task is being
+            // launched or moved to the front which may cause this activity to try and enter PiP
+            // when it is next made invisible.
+    float pictureInPictureAspectRatio; // The aspect ratio to use when auto-entering
+            // picture-in-picture
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
     ComponentName requestedVrComponent; // the requested component for handling VR mode.
@@ -434,6 +440,11 @@
         if (info != null) {
             pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
         }
+        if (supportsPipOnMoveToBackground) {
+            pw.println(prefix + "supportsPipOnMoveToBackground=1 "
+                    + "enterPipOnMoveToBackground="
+                            + (enterPipOnMoveToBackground ? 1 : 0));
+        }
     }
 
     private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
@@ -842,6 +853,23 @@
     }
 
     /**
+     * If this activity has requested that it auto-enter picture-in-picture and we can actually do
+     * this, then mark it to enter picture in picture at that point.
+     */
+    void setEnterPipOnMoveToBackground(boolean enterPipOnInvisible) {
+        if (supportsPipOnMoveToBackground) {
+            enterPipOnMoveToBackground = enterPipOnInvisible;
+        }
+    }
+
+    /**
+     * @return whether to enter PiP when this activity is made invisible.
+     */
+    public boolean shouldEnterPictureInPictureOnInvisible() {
+        return enterPipOnMoveToBackground;
+    }
+
+    /**
      * @return Stack value from current task, null if there is no task.
      */
     ActivityStack getStack() {
@@ -922,10 +950,34 @@
                 && info.resizeMode != RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
     }
 
+    /**
+     * @return whether this activity's resize mode supports PIP.
+     */
     boolean supportsPictureInPicture() {
         return !isHomeActivity() && info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
     }
 
+    /**
+     * @return whether this activity is currently allowed to enter PIP, if
+     * {@param checkActivityVisibility} is set, then the current activity visibility is taken into
+     * account.
+     */
+    boolean canEnterPictureInPicture(boolean checkActivityVisibility) {
+        if (!checkActivityVisibility) {
+            return supportsPictureInPicture();
+        }
+
+        if (supportsPictureInPicture() && visible) {
+            switch (state) {
+                case RESUMED:
+                case PAUSING:
+                case PAUSED:
+                    return true;
+            }
+        }
+        return false;
+    }
+
     boolean canGoInDockedStack() {
         return !isHomeActivity() && isResizeableOrForced();
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d94d3cd..d160a46 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -22,7 +22,6 @@
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
@@ -723,7 +722,6 @@
 
         mStacks.remove(this);
         int addIndex = mStacks.size();
-
         if (addIndex > 0) {
             final ActivityStack topStack = mStacks.get(addIndex - 1);
             if (StackId.isAlwaysOnTop(topStack.mStackId) && topStack != this) {
@@ -1718,7 +1716,9 @@
                                 + stackInvisible + " behindFullscreenActivity="
                                 + behindFullscreenActivity + " mLaunchTaskBehind="
                                 + r.mLaunchTaskBehind);
-                        makeInvisible(r, visibleBehind);
+                        if (!enterPictureInPictureOnActivityInvisible(r)) {
+                            makeInvisible(r, visibleBehind);
+                        }
                     }
                 }
                 if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
@@ -1876,6 +1876,32 @@
         return false;
     }
 
+    /**
+     * Attempts to enter picture-in-picture if the activity that is being made invisible supports
+     * it.  If not, then
+     *
+     * @return whether or not picture-in-picture mode was entered.
+     */
+    private boolean enterPictureInPictureOnActivityInvisible(ActivityRecord r) {
+        final boolean hasPinnedStack =
+                mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, " enterPictureInPictureOnInvisible="
+                + r.shouldEnterPictureInPictureOnInvisible()
+                + " hasPinnedStack=" + hasPinnedStack);
+        if (!hasPinnedStack && r.visible && r.shouldEnterPictureInPictureOnInvisible()) {
+            r.setEnterPipOnMoveToBackground(false);
+
+            // Enter picture in picture, but don't move the home stack to the front
+            // since it will affect the focused stack's visibility and occlude
+            // starting activities
+            mService.enterPictureInPictureModeLocked(r, r.getDisplayId(),
+                    r.pictureInPictureAspectRatio, false /* moveHomeStackToFront */,
+                    "ensureActivitiesVisibleLocked");
+            return true;
+        }
+        return false;
+    }
+
     private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
         if (!r.visible) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
@@ -2613,8 +2639,8 @@
         mWindowManager.moveTaskToTop(task.taskId);
     }
 
-    final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
-            ActivityOptions options) {
+    final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         TaskRecord rTask = r.task;
         final int taskId = rTask.taskId;
         // mLaunchTaskBehind tasks get placed at the back of the task stack.
@@ -2693,11 +2719,20 @@
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
                 mNoAnimActivities.add(r);
             } else {
-                mWindowManager.prepareAppTransition(newTask
-                        ? r.mLaunchTaskBehind
-                                ? TRANSIT_TASK_OPEN_BEHIND
-                                : TRANSIT_TASK_OPEN
-                        : TRANSIT_ACTIVITY_OPEN, keepCurTransition);
+                int transit = TRANSIT_ACTIVITY_OPEN;
+                if (newTask) {
+                    if (r.mLaunchTaskBehind) {
+                        transit = TRANSIT_TASK_OPEN_BEHIND;
+                    } else {
+                        // If a new task is being launched, then mark the existing top activity to
+                        // enter picture-in-picture if it supports auto-entering PiP
+                        if (focusedTopActivity != null) {
+                            focusedTopActivity.setEnterPipOnMoveToBackground(true);
+                        }
+                        transit = TRANSIT_TASK_OPEN;
+                    }
+                }
+                mWindowManager.prepareAppTransition(transit, keepCurTransition);
                 mNoAnimActivities.remove(r);
             }
             addConfigOverride(r, task);
@@ -4193,6 +4228,8 @@
             AppTimeTracker timeTracker, String reason) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
 
+        final ActivityRecord focusedTopActivity = mStackSupervisor.getFocusedStack() != null
+                ? mStackSupervisor.getFocusedStack().topActivity() : null;
         final int numTasks = mTaskHistory.size();
         final int index = mTaskHistory.indexOf(tr);
         if (numTasks == 0 || index < 0)  {
@@ -4238,6 +4275,11 @@
         } else {
             updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
         }
+        // If a new task is moved to the front, then mark the existing top activity to enter
+        // picture-in-picture if it supports auto-entering PiP
+        if (focusedTopActivity != null) {
+            focusedTopActivity.setEnterPipOnMoveToBackground(true);
+        }
 
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
         EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index dde948f..281812c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2322,6 +2322,11 @@
             return true;
         }
 
+        if (!task.canResizeToBounds(bounds)) {
+            throw new IllegalArgumentException("resizeTaskLocked: Can not resize task=" + task
+                    + " to bounds=" + bounds + " resizeMode=" + task.mResizeMode);
+        }
+
         // Do not move the task to another stack here.
         // This method assumes that the task is already placed in the right stack.
         // we do not mess with that decision and we only do the resize!
@@ -2638,11 +2643,13 @@
             return false;
         }
 
-        moveActivityToPinnedStackLocked(r, "moveTopActivityToPinnedStack", bounds);
+        moveActivityToPinnedStackLocked(r, "moveTopActivityToPinnedStack", bounds,
+                true /* moveHomeStackToFront */);
         return true;
     }
 
-    void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds) {
+    void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds,
+            boolean moveHomeStackToFront) {
         mWindowManager.deferSurfaceLayout();
         try {
             final TaskRecord task = r.task;
@@ -2666,7 +2673,7 @@
             if (task.mActivities.size() == 1) {
                 // There is only one activity in the task. So, we can just move the task over to
                 // the stack without re-parenting the activity in a different task.
-                if (task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+                if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
                     // Move the home stack forward if the task we just moved to the pinned stack
                     // was launched from home so home should be visible behind it.
                     moveHomeStackToFront(reason);
@@ -2674,6 +2681,8 @@
                 moveTaskToStackLocked(
                         task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
             } else {
+                // There are multiple activities in the task and moving the top activity should
+                // reveal/leave the other activities in their original task
                 stack.moveActivityToStack(r);
             }
         } finally {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 64bf3ad..d0960a0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1068,6 +1068,7 @@
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
         final ActivityStack topStack = mSupervisor.mFocusedStack;
+        final ActivityRecord topFocused = topStack.topActivity();
         final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
         final boolean dontStart = top != null && mStartActivity.resultTo == null
                 && top.realActivity.equals(mStartActivity.realActivity)
@@ -1139,7 +1140,8 @@
 
         sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
 
-        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
+        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
+                mOptions);
         if (mDoResume) {
             final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
             if (!mTargetStack.isFocusable()
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 5c352e1..383f106 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -73,6 +73,9 @@
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
@@ -1077,12 +1080,32 @@
                 && !mTemporarilyUnresizable;
     }
 
+    /**
+     * Check that a given bounds matches the application requested orientation.
+     *
+     * @param bounds The bounds to be tested.
+     * @return True if the requested bounds are okay for a resizing request.
+     */
+    boolean canResizeToBounds(Rect bounds) {
+        if (bounds == null || getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
+            // Note: If not on the freeform workspace, we ignore the bounds.
+            return true;
+        }
+        final boolean landscape = bounds.width() > bounds.height();
+        if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) {
+            return mBounds == null || landscape == (mBounds.width() > mBounds.height());
+        }
+        return (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY || !landscape)
+                && (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY || landscape);
+    }
+
     boolean isOnTopLauncher() {
         return isHomeTask() && mIsOnTopLauncher;
     }
 
     boolean canGoInDockedStack() {
-        return isResizeable();
+        return isResizeable() &&
+                !ActivityInfo.isPreserveOrientationMode(mResizeMode);
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c78a0f5..f58522b5 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -110,6 +110,7 @@
 import android.service.notification.NotificationAssistantService;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
+import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
 import android.telephony.PhoneStateListener;
@@ -1020,7 +1021,7 @@
                 synchronized (mNotificationList) {
                     addAutogroupKeyLocked(key);
                 }
-                mRankingHandler.requestSort();
+                mRankingHandler.requestSort(false);
             }
 
             @Override
@@ -1028,7 +1029,7 @@
                 synchronized (mNotificationList) {
                     removeAutogroupKeyLocked(key);
                 }
-                mRankingHandler.requestSort();
+                mRankingHandler.requestSort(false);
             }
 
             @Override
@@ -2390,7 +2391,7 @@
                     mNotificationAssistants.checkServiceTokenLocked(token);
                     applyAdjustmentLocked(adjustment);
                 }
-                mRankingHandler.requestSort();
+                mRankingHandler.requestSort(true);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2408,7 +2409,7 @@
                         applyAdjustmentLocked(adjustment);
                     }
                 }
-                mRankingHandler.requestSort();
+                mRankingHandler.requestSort(true);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2420,12 +2421,20 @@
         if (n == null) {
             return;
         }
-        if (adjustment.getImportance() != IMPORTANCE_NONE) {
-            n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
-        }
         if (adjustment.getSignals() != null) {
             Bundle.setDefusable(adjustment.getSignals(), true);
-            // TODO: apply signals
+            final String overrideChannelId =
+                    adjustment.getSignals().getString(Adjustment.KEY_CHANNEL_ID, null);
+            final ArrayList<String> people =
+                    adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
+            final ArrayList<SnoozeCriterion> snoozeCriterionList =
+                    adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
+            if (!TextUtils.isEmpty(overrideChannelId)) {
+                n.setNotificationChannelOverride(mRankingHelper.getNotificationChannel(
+                        n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId));
+            }
+            n.setPeopleOverride(people);
+            n.setSnoozeCriteria(snoozeCriterionList);
         }
     }
 
@@ -3309,27 +3318,27 @@
         }
     }
 
-    private void handleRankingSort() {
+    private void handleRankingSort(Message msg) {
+        if (!(msg.obj instanceof Boolean)) return;
+        boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
         synchronized (mNotificationList) {
             final int N = mNotificationList.size();
             ArrayList<String> orderBefore = new ArrayList<String>(N);
             ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
-            int[] importances = new int[N];
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
                 groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
-                importances[i] = r.getImportance();
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
-                if (!orderBefore.get(i).equals(r.getKey())
+                if (forceUpdate
+                        || !orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || importances[i] != r.getImportance()
                         || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
                     scheduleSendRankingUpdate();
                     return;
@@ -3439,14 +3448,17 @@
                     handleRankingReconsideration(msg);
                     break;
                 case MESSAGE_RANKING_SORT:
-                    handleRankingSort();
+                    handleRankingSort(msg);
                     break;
             }
         }
 
-        public void requestSort() {
+        public void requestSort(boolean forceUpdate) {
             removeMessages(MESSAGE_RANKING_SORT);
-            sendEmptyMessage(MESSAGE_RANKING_SORT);
+            Message msg = Message.obtain();
+            msg.what = MESSAGE_RANKING_SORT;
+            msg.obj = forceUpdate;
+            sendMessage(msg);
         }
 
         public void requestReconsideration(RankingReconsideration recon) {
@@ -3987,6 +3999,9 @@
         Bundle visibilityOverrides = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
+        Bundle overrideChannels = new Bundle();
+        Bundle overridePeople = new Bundle();
+        Bundle snoozeCriteria = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -4008,6 +4023,9 @@
                 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
             overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
+            overrideChannels.putParcelable(key, record.getChannel());
+            overridePeople.putStringArrayList(key, record.getPeopleOverride());
+            snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -4017,7 +4035,8 @@
             importanceAr[i] = importance.get(i);
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
-                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
+                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
+                overrideChannels, overridePeople, snoozeCriteria);
     }
 
     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0213258..4fcc987 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -36,6 +36,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.util.Slog;
@@ -45,6 +46,7 @@
 
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -110,6 +112,9 @@
     private Uri mSound;
     private long[] mVibration;
     private AudioAttributes mAttributes;
+    private NotificationChannel mOverrideChannel;
+    private ArrayList<String> mPeopleOverride;
+    private ArrayList<SnoozeCriterion> mSnoozeCriteria;
 
     @VisibleForTesting
     public NotificationRecord(Context context, StatusBarNotification sbn)
@@ -626,7 +631,14 @@
     }
 
     public NotificationChannel getChannel() {
-        return sbn.getNotificationChannel();
+        return mOverrideChannel == null ? sbn.getNotificationChannel() : mOverrideChannel;
+    }
+
+    protected void setNotificationChannelOverride(NotificationChannel channel) {
+        mOverrideChannel = channel;
+        if (mOverrideChannel != null) {
+            calculateImportance();
+        }
     }
 
     public Uri getSound() {
@@ -640,4 +652,20 @@
     public AudioAttributes getAudioAttributes() {
         return mAttributes;
     }
+
+    public ArrayList<String> getPeopleOverride() {
+        return mPeopleOverride;
+    }
+
+    protected void setPeopleOverride(ArrayList<String> people) {
+        mPeopleOverride = people;
+    }
+
+    public ArrayList<SnoozeCriterion> getSnoozeCriteria() {
+        return mSnoozeCriteria;
+    }
+
+    protected void setSnoozeCriteria(ArrayList<SnoozeCriterion> snoozeCriteria) {
+        mSnoozeCriteria = snoozeCriteria;
+    }
 }
diff --git a/services/core/java/com/android/server/notification/RankingHandler.java b/services/core/java/com/android/server/notification/RankingHandler.java
index 80bb4f0..656d727 100644
--- a/services/core/java/com/android/server/notification/RankingHandler.java
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -16,6 +16,6 @@
 package com.android.server.notification;
 
 public interface RankingHandler {
-    public void requestSort();
+    public void requestSort(boolean forceUpdate);
     public void requestReconsideration(RankingReconsideration recon);
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index a41231d..5073b1b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -328,7 +328,7 @@
         for (int i = 0; i < N; i++) {
             mSignalExtractors[i].setConfig(this);
         }
-        mRankingHandler.requestSort();
+        mRankingHandler.requestSort(false);
     }
 
     public void sort(ArrayList<NotificationRecord> notificationList) {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index c9e1315..5dd651f 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -31,13 +31,17 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.LruCache;
 import android.util.Slog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
@@ -157,7 +161,8 @@
         if (context == null) {
             return NONE;
         }
-        final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut);
+        final PeopleRankingReconsideration prr =
+                validatePeople(context, key, extras, null, affinityOut);
         float affinity = affinityOut[0];
 
         if (prr != null) {
@@ -207,7 +212,8 @@
         final String key = record.getKey();
         final Bundle extras = record.getNotification().extras;
         final float[] affinityOut = new float[1];
-        final PeopleRankingReconsideration rr = validatePeople(context, key, extras, affinityOut);
+        final PeopleRankingReconsideration rr =
+                validatePeople(context, key, extras, record.getPeopleOverride(), affinityOut);
         final float affinity = affinityOut[0];
         record.setContactAffinity(affinity);
         if (rr == null) {
@@ -220,22 +226,22 @@
     }
 
     private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
-            float[] affinityOut) {
+            List<String> peopleOverride, float[] affinityOut) {
         long start = SystemClock.elapsedRealtime();
         float affinity = NONE;
         if (extras == null) {
             return null;
         }
-
-        final String[] people = getExtraPeople(extras);
-        if (people == null || people.length == 0) {
-            return null;
+        final Set<String> people = new ArraySet<>(peopleOverride);
+        final String[] notificationPeople = getExtraPeople(extras);
+        if (notificationPeople != null ) {
+            people.addAll(Arrays.asList(getExtraPeople(extras)));
         }
 
         if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
         final LinkedList<String> pendingLookups = new LinkedList<String>();
-        for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
-            final String handle = people[personIdx];
+        int personIdx = 0;
+        for (String handle : people) {
             if (TextUtils.isEmpty(handle)) continue;
 
             synchronized (mPeopleCache) {
@@ -250,6 +256,9 @@
                     affinity = Math.max(affinity, lookupResult.getAffinity());
                 }
             }
+            if (++personIdx == MAX_PEOPLE) {
+                break;
+            }
         }
 
         // record the best available data, so far:
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index d8f6b80..6aeb49c 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -26,15 +26,11 @@
 import android.os.ServiceSpecificException;
 import android.util.Slog;
 
-import com.android.internal.os.InstallerConnection;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.server.SystemService;
 
 import dalvik.system.VMRuntime;
 
-import java.util.Arrays;
-
-public final class Installer extends SystemService {
+public class Installer extends SystemService {
     private static final String TAG = "Installer";
 
     /* ***************************************************************************
@@ -51,32 +47,29 @@
     public static final int DEXOPT_BOOTCOMPLETE   = 1 << 4;
     /** Hint that the dexopt type is profile-guided. */
     public static final int DEXOPT_PROFILE_GUIDED = 1 << 5;
-    /** This is an OTA update dexopt */
-    public static final int DEXOPT_OTA            = 1 << 6;
 
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
     public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
 
-    private final InstallerConnection mInstaller;
-    private final IInstalld mInstalld;
+    private final boolean mIsolated;
 
+    // TODO: reconnect if installd restarts
+    private volatile IInstalld mInstalld;
     private volatile Object mWarnIfHeld;
 
     public Installer(Context context) {
-        super(context);
-        mInstaller = new InstallerConnection();
-        // TODO: reconnect if installd restarts
-        mInstalld = IInstalld.Stub.asInterface(ServiceManager.getService("installd"));
+        this(context, false);
     }
 
-    // Package-private installer that accepts a custom InstallerConnection. Used for
-    // OtaDexoptService.
-    Installer(Context context, InstallerConnection connection) {
+    /**
+     * @param isolated indicates if this object should <em>not</em> connect to
+     *            the real {@code installd}. All remote calls will be ignored
+     *            unless you extend this class and intercept them.
+     */
+    public Installer(Context context, boolean isolated) {
         super(context);
-        mInstaller = connection;
-        // TODO: reconnect if installd restarts
-        mInstalld = IInstalld.Stub.asInterface(ServiceManager.getService("installd"));
+        mIsolated = isolated;
     }
 
     /**
@@ -84,183 +77,239 @@
      * the given object.
      */
     public void setWarnIfHeld(Object warnIfHeld) {
-        mInstaller.setWarnIfHeld(warnIfHeld);
         mWarnIfHeld = warnIfHeld;
     }
 
     @Override
     public void onStart() {
-        Slog.i(TAG, "Waiting for installd to be ready.");
-        mInstaller.waitForConnection();
+        if (mIsolated) {
+            mInstalld = null;
+        } else {
+            mInstalld = IInstalld.Stub.asInterface(ServiceManager.getService("installd"));
+        }
     }
 
-    private void checkLock() {
+    /**
+     * Do several pre-flight checks before making a remote call.
+     *
+     * @return if the remote call should continue.
+     */
+    private boolean checkBeforeRemote() {
         if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
                     + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
         }
+        if (mIsolated) {
+            Slog.i(TAG, "Ignoring request because this installer is isolated");
+            return false;
+        } else {
+            return true;
+        }
     }
 
     public void createAppData(String uuid, String packageName, int userId, int flags, int appId,
             String seInfo, int targetSdkVersion) throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
                     targetSdkVersion);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void restoreconAppData(String uuid, String packageName, int userId, int flags, int appId,
             String seInfo) throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.restoreconAppData(uuid, packageName, userId, flags, appId, seInfo);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void migrateAppData(String uuid, String packageName, int userId, int flags)
             throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.migrateAppData(uuid, packageName, userId, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void clearAppData(String uuid, String packageName, int userId, int flags,
             long ceDataInode) throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void destroyAppData(String uuid, String packageName, int userId, int flags,
             long ceDataInode) throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.destroyAppData(uuid, packageName, userId, flags, ceDataInode);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void moveCompleteApp(String fromUuid, String toUuid, String packageName,
             String dataAppName, int appId, String seInfo, int targetSdkVersion)
             throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo,
                     targetSdkVersion);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
-    public void getAppSize(String uuid, String pkgname, int userid, int flags, long ceDataInode,
+    public void getAppSize(String uuid, String packageName, int userId, int flags, long ceDataInode,
             String codePath, PackageStats stats) throws InstallerException {
-        final String[] res = mInstaller.execute("get_app_size", uuid, pkgname, userid, flags,
-                ceDataInode, codePath);
+        if (!checkBeforeRemote()) return;
         try {
-            stats.codeSize += Long.parseLong(res[1]);
-            stats.dataSize += Long.parseLong(res[2]);
-            stats.cacheSize += Long.parseLong(res[3]);
-        } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
-            throw new InstallerException("Invalid size result: " + Arrays.toString(res));
+            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, ceDataInode,
+                    codePath);
+            stats.codeSize += res[0];
+            stats.dataSize += res[1];
+            stats.cacheSize += res[2];
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
         }
     }
 
     public long getAppDataInode(String uuid, String packageName, int userId, int flags)
             throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return -1;
         try {
             return mInstalld.getAppDataInode(uuid, packageName, userId, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
-    public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
-            int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries)
-            throws InstallerException {
-        assertValidInstructionSet(instructionSet);
-        mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags,
-                compilerFilter, volumeUuid, sharedLibraries);
-    }
-
-    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+    public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
-            String compilerFilter, String volumeUuid, String sharedLibraries)
+            String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries)
             throws InstallerException {
         assertValidInstructionSet(instructionSet);
-        mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
-                outputPath, dexFlags, compilerFilter, volumeUuid, sharedLibraries);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
+                    dexFlags, compilerFilter, volumeUuid, sharedLibraries);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
-    public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
-        return mInstaller.mergeProfiles(uid, pkgName);
+    public boolean mergeProfiles(int uid, String packageName) throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+        try {
+            return mInstalld.mergeProfiles(uid, packageName);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
-    public boolean dumpProfiles(String gid, String packageName, String codePaths)
+    public boolean dumpProfiles(int uid, String packageName, String codePaths)
             throws InstallerException {
-        return mInstaller.dumpProfiles(gid, packageName, codePaths);
+        if (!checkBeforeRemote()) return false;
+        try {
+            return mInstalld.dumpProfiles(uid, packageName, codePaths);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void idmap(String targetApkPath, String overlayApkPath, int uid)
             throws InstallerException {
-        mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.idmap(targetApkPath, overlayApkPath, uid);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void rmdex(String codePath, String instructionSet) throws InstallerException {
         assertValidInstructionSet(instructionSet);
-        mInstaller.execute("rmdex", codePath, instructionSet);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.rmdex(codePath, instructionSet);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void rmPackageDir(String packageDir) throws InstallerException {
-        mInstaller.execute("rmpackagedir", packageDir);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.rmPackageDir(packageDir);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
-    public void clearAppProfiles(String pkgName) throws InstallerException {
-        mInstaller.execute("clear_app_profiles", pkgName);
+    public void clearAppProfiles(String packageName) throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.clearAppProfiles(packageName);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
-    public void destroyAppProfiles(String pkgName) throws InstallerException {
-        mInstaller.execute("destroy_app_profiles", pkgName);
+    public void destroyAppProfiles(String packageName) throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.destroyAppProfiles(packageName);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void createUserData(String uuid, int userId, int userSerial, int flags)
             throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.createUserData(uuid, userId, userSerial, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
-        checkLock();
+        if (!checkBeforeRemote()) return;
         try {
             mInstalld.destroyUserData(uuid, userId, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
     public void markBootComplete(String instructionSet) throws InstallerException {
         assertValidInstructionSet(instructionSet);
-        mInstaller.execute("markbootcomplete", instructionSet);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.markBootComplete(instructionSet);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
-        mInstaller.execute("freecache", uuid, freeStorageSize);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.freeCache(uuid, freeStorageSize);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     /**
@@ -268,29 +317,54 @@
      * directory to the real location for backward compatibility. Note that no
      * such symlink is created for 64 bit shared libraries.
      */
-    public void linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
+    public void linkNativeLibraryDirectory(String uuid, String packageName, String nativeLibPath32,
             int userId) throws InstallerException {
-        mInstaller.execute("linklib", uuid, dataPath, nativeLibPath32, userId);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.linkNativeLibraryDirectory(uuid, packageName, nativeLibPath32, userId);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void createOatDir(String oatDir, String dexInstructionSet)
             throws InstallerException {
-        mInstaller.execute("createoatdir", oatDir, dexInstructionSet);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.createOatDir(oatDir, dexInstructionSet);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void linkFile(String relativePath, String fromBase, String toBase)
             throws InstallerException {
-        mInstaller.execute("linkfile", relativePath, fromBase, toBase);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.linkFile(relativePath, fromBase, toBase);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void moveAb(String apkPath, String instructionSet, String outputPath)
             throws InstallerException {
-        mInstaller.execute("move_ab", apkPath, instructionSet, outputPath);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.moveAb(apkPath, instructionSet, outputPath);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public void deleteOdex(String apkPath, String instructionSet, String outputPath)
             throws InstallerException {
-        mInstaller.execute("delete_odex", apkPath, instructionSet, outputPath);
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     private static void assertValidInstructionSet(String instructionSet)
@@ -302,4 +376,14 @@
         }
         throw new InstallerException("Invalid instruction set: " + instructionSet);
     }
+
+    public static class InstallerException extends Exception {
+        public InstallerException(String detailMessage) {
+            super(detailMessage);
+        }
+
+        public static InstallerException from(Exception e) throws InstallerException {
+            throw new InstallerException(e.getMessage());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index f777aae..bbd227e 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -16,11 +16,11 @@
 
 package com.android.server.pm;
 
-import static com.android.server.pm.Installer.DEXOPT_OTA;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.IOtaDexopt;
 import android.content.pm.PackageParser;
@@ -30,15 +30,17 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.storage.StorageManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
+
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.InstallerConnection;
-import com.android.internal.os.InstallerConnection.InstallerException;
+import com.android.server.pm.Installer.InstallerException;
 
 import java.io.File;
 import java.io.FileDescriptor;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -277,9 +279,27 @@
      */
     private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
             int compilationReason) {
-        // Use our custom connection that just collects the commands.
-        RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
-        Installer collectingInstaller = new Installer(mContext, collectingConnection);
+        // Intercept and collect dexopt requests
+        final List<String> commands = new ArrayList<String>();
+        final Installer collectingInstaller = new Installer(mContext, true) {
+            @Override
+            public void dexopt(String apkPath, int uid, @Nullable String pkgName,
+                    String instructionSet, int dexoptNeeded, @Nullable String outputPath,
+                    int dexFlags, String compilerFilter, @Nullable String volumeUuid,
+                    @Nullable String sharedLibraries) throws InstallerException {
+                commands.add(buildCommand("dexopt",
+                        apkPath,
+                        uid,
+                        pkgName,
+                        instructionSet,
+                        dexoptNeeded,
+                        outputPath,
+                        dexFlags,
+                        compilerFilter,
+                        volumeUuid,
+                        sharedLibraries));
+            }
+        };
 
         // Use the package manager install and install lock here for the OTA dex optimizer.
         PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
@@ -296,7 +316,7 @@
                 getCompilerFilterForReason(compilationReason),
                 null /* CompilerStats.PackageStats */);
 
-        return collectingConnection.commands;
+        return commands;
     }
 
     @Override
@@ -401,53 +421,33 @@
 
     private static class OTADexoptPackageDexOptimizer extends
             PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
-
         public OTADexoptPackageDexOptimizer(Installer installer, Object installLock,
                 Context context) {
             super(installer, installLock, context, "*otadexopt*");
         }
-
-        @Override
-        protected int adjustDexoptFlags(int dexoptFlags) {
-            // Add the OTA flag.
-            return dexoptFlags | DEXOPT_OTA;
-        }
-
     }
 
-    private static class RecordingInstallerConnection extends InstallerConnection {
-        public List<String> commands = new ArrayList<String>(1);
-
-        @Override
-        public void setWarnIfHeld(Object warnIfHeld) {
-            throw new IllegalStateException("Should not reach here");
+    /**
+     * Cook up argument list in the format that {@code installd} expects.
+     */
+    private static String buildCommand(Object... args) {
+        final StringBuilder builder = new StringBuilder();
+        for (Object arg : args) {
+            String escaped;
+            if (arg == null) {
+                escaped = "";
+            } else {
+                escaped = String.valueOf(arg);
+            }
+            if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
+                throw new IllegalArgumentException(
+                        "Invalid argument while executing " + Arrays.toString(args));
+            }
+            if (TextUtils.isEmpty(escaped)) {
+                escaped = "!";
+            }
+            builder.append(' ').append(escaped);
         }
-
-        @Override
-        public synchronized String transact(String cmd) {
-            commands.add(cmd);
-            return "0";
-        }
-
-        @Override
-        public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
-            throw new IllegalStateException("Should not reach here");
-        }
-
-        @Override
-        public boolean dumpProfiles(String gid, String packageName, String codePaths)
-                throws InstallerException {
-            throw new IllegalStateException("Should not reach here");
-        }
-
-        @Override
-        public void disconnect() {
-            throw new IllegalStateException("Should not reach here");
-        }
-
-        @Override
-        public void waitForConnection() {
-            throw new IllegalStateException("Should not reach here");
-        }
+        return builder.toString();
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index acdcc72..2e0199b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -27,8 +27,8 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.pm.Installer.InstallerException;
 
 import java.io.File;
 import java.io.IOException;
@@ -251,7 +251,7 @@
                 Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                         + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                         + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
-                        + " target-filter=" + targetCompilerFilter + " oatDir = " + oatDir
+                        + " target-filter=" + targetCompilerFilter + " oatDir=" + oatDir
                         + " sharedLibraries=" + sharedLibrariesPath);
                 // Profile guide compiled oat files should not be public.
                 final boolean isPublic = !pkg.isForwardLocked() && !isProfileGuidedFilter;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2ece99f..c85e1d8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -72,10 +72,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 
 import java.io.File;
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index f243e63..0e3f173 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -19,7 +19,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.PackageParserException;
 
-import com.android.internal.os.InstallerConnection.InstallerException;
+import com.android.server.pm.Installer.InstallerException;
 
 /** {@hide} */
 public class PackageManagerException extends Exception {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 25f0677..ed405e9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -237,7 +237,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.IParcelFileDescriptorFactory;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
@@ -257,6 +256,7 @@
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
@@ -544,7 +544,6 @@
             Manifest.permission.ACCESS_COARSE_LOCATION,
             Manifest.permission.RECORD_AUDIO,
             Manifest.permission.READ_PHONE_STATE,
-            Manifest.permission.READ_PHONE_NUMBER,
             Manifest.permission.CALL_PHONE,
             Manifest.permission.READ_CALL_LOG,
             Manifest.permission.WRITE_CALL_LOG,
@@ -559,7 +558,8 @@
             Manifest.permission.RECEIVE_WAP_PUSH,
             Manifest.permission.RECEIVE_MMS,
             Manifest.permission.READ_EXTERNAL_STORAGE,
-            Manifest.permission.WRITE_EXTERNAL_STORAGE);
+            Manifest.permission.WRITE_EXTERNAL_STORAGE,
+            Manifest.permission.READ_PHONE_NUMBER);
 
     final ServiceThread mHandlerThread;
 
@@ -2229,6 +2229,18 @@
             mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
+            // Clean up orphaned packages for which the code path doesn't exist
+            // and they are an update to a system app - caused by bug/32321269
+            final int packageSettingCount = mSettings.mPackages.size();
+            for (int i = packageSettingCount - 1; i >= 0; i--) {
+                PackageSetting ps = mSettings.mPackages.valueAt(i);
+                if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
+                        && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
+                    mSettings.mPackages.removeAt(i);
+                    mSettings.enableSystemPackageLPw(ps.name);
+                }
+            }
+
             if (mFirstBoot) {
                 requestCopyPreoptedFiles();
             }
@@ -2287,8 +2299,9 @@
                                     getCompilerFilterForReason(REASON_SHARED_APK),
                                     false /* newProfile */);
                             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                                mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
-                                        dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
+                                mInstaller.dexopt(lib, Process.SYSTEM_UID, "*",
+                                        dexCodeInstructionSet, dexoptNeeded, null,
+                                        DEXOPT_PUBLIC,
                                         getCompilerFilterForReason(REASON_SHARED_APK),
                                         StorageManager.UUID_PRIVATE_INTERNAL,
                                         SKIP_SHARED_LIBRARY_CHECK);
@@ -3209,8 +3222,12 @@
         flags = updateFlagsForPackage(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */, "get package info");
+
         // reader
         synchronized (mPackages) {
+            // Normalize package name to hanlde renamed packages
+            packageName = normalizePackageNameLPr(packageName);
+
             final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
             PackageParser.Package p = null;
             if (matchFactoryOnly) {
@@ -3413,8 +3430,12 @@
         flags = updateFlagsForApplication(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */, "get application info");
+
         // writer
         synchronized (mPackages) {
+            // Normalize package name to hanlde renamed packages
+            packageName = normalizePackageNameLPr(packageName);
+
             PackageParser.Package p = mPackages.get(packageName);
             if (DEBUG_PACKAGE_INFO) Log.v(
                     TAG, "getApplicationInfo " + packageName
@@ -3436,6 +3457,11 @@
         return null;
     }
 
+    private String normalizePackageNameLPr(String packageName) {
+        String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
+        return normalizedPackageName != null ? normalizedPackageName : packageName;
+    }
+
     @Override
     public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
             final IPackageDataObserver observer) {
@@ -7733,9 +7759,8 @@
             final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
             try {
                 List<String> allCodePaths = pkg.getAllCodePathsExcludingResourceOnly();
-                String gid = Integer.toString(sharedGid);
                 String codePaths = TextUtils.join(";", allCodePaths);
-                mInstaller.dumpProfiles(gid, packageName, codePaths);
+                mInstaller.dumpProfiles(sharedGid, packageName, codePaths);
             } catch (InstallerException e) {
                 Slog.w(TAG, "Failed to dump profiles", e);
             }
@@ -20105,6 +20130,9 @@
     private void assertPackageKnown(String volumeUuid, String packageName)
             throws PackageManagerException {
         synchronized (mPackages) {
+            // Normalize package name to handle renamed packages
+            packageName = normalizePackageNameLPr(packageName);
+
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
                 throw new PackageManagerException("Package " + packageName + " is unknown");
@@ -20119,6 +20147,9 @@
     private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
             throws PackageManagerException {
         synchronized (mPackages) {
+            // Normalize package name to handle renamed packages
+            packageName = normalizePackageNameLPr(packageName);
+
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
                 throw new PackageManagerException("Package " + packageName + " is unknown");
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7938a12..2751742 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1109,7 +1109,7 @@
                     sessionParams.abiOverride = checkAbiArgument(getNextArg());
                     break;
                 case "--ephemeral":
-                    sessionParams.installFlags |= PackageManager.INSTALL_EPHEMERAL;
+                    sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
                     break;
                 case "--user":
                     params.userId = UserHandle.parseUserArg(getNextArgRequired());
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e330585..7187fc2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -80,13 +80,13 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.server.backup.PreferredActivityBackupHelper;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageManagerService.DumpState;
 import com.android.server.pm.PermissionsState.PermissionState;
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f48db05..afad328 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7600,6 +7600,32 @@
         return vis;
     }
 
+    private int updateLightNavigationBarLw(int vis, WindowState opaque,
+            WindowState opaqueOrDimming) {
+        final WindowState imeWin = mWindowManagerFuncs.getInputMethodWindowLw();
+
+        final WindowState navColorWin;
+        if (imeWin != null && imeWin.isVisibleLw()) {
+            navColorWin = imeWin;
+        } else {
+            navColorWin = opaqueOrDimming;
+        }
+
+        if (navColorWin != null) {
+            if (navColorWin == opaque) {
+                // If the top fullscreen-or-dimming window is also the top fullscreen, respect
+                // its light flag.
+                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+                vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
+                        & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+            } else if (navColorWin.isDimming() || navColorWin == imeWin) {
+                // Otherwise if it's dimming or it's the IME window, clear the light flag.
+                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+            }
+        }
+        return vis;
+    }
+
     private boolean drawsSystemBarBackground(WindowState win) {
         return win == null || (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
     }
@@ -7730,6 +7756,9 @@
 
         vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
 
+        vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
+                mTopFullscreenOpaqueOrDimmingWindowState);
+
         return vis;
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9d02940..cca8cc8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -169,7 +169,7 @@
         CharSequence label;
         Drawable icon;
         ComponentName component; // service that implements ITrustAgent
-        ComponentName settings; // setting to launch to modify agent.
+        SettingsAttrs settings; // setting to launch to modify agent.
         TrustAgentWrapper agent;
         int userId;
 
@@ -258,11 +258,6 @@
                         + ": switchToByUser=false");
                 continue;
             }
-            if (!StorageManager.isUserKeyUnlocked(userInfo.id)) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": FDE still locked");
-                continue;
-            }
             if (!mActivityManager.isUserRunning(userInfo.id)) {
                 if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
                         + ": user not started");
@@ -273,13 +268,7 @@
                         + ": no secure credential");
                 continue;
             }
-            if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": prevented by StrongAuthTracker = 0x"
-                        + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
-                        userInfo.id)));
-                continue;
-            }
+
             DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
             int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
             final boolean disableTrustAgents =
@@ -312,16 +301,49 @@
                         continue;
                     }
                 }
-
                 AgentInfo agentInfo = new AgentInfo();
                 agentInfo.component = name;
                 agentInfo.userId = userInfo.id;
                 if (!mActiveAgents.contains(agentInfo)) {
                     agentInfo.label = resolveInfo.loadLabel(pm);
                     agentInfo.icon = resolveInfo.loadIcon(pm);
-                    agentInfo.settings = getSettingsComponentName(pm, resolveInfo);
+                    agentInfo.settings = getSettingsAttrs(pm, resolveInfo);
                     agentInfo.agent = new TrustAgentWrapper(mContext, this,
                             new Intent().setComponent(name), userInfo.getUserHandle());
+                } else {
+                    int index = mActiveAgents.indexOf(agentInfo);
+                    agentInfo = mActiveAgents.valueAt(index);
+                }
+
+                boolean directUnlock = resolveInfo.serviceInfo.directBootAware
+                    && agentInfo.settings.canUnlockProfile;
+
+                if (directUnlock) {
+                    if (DEBUG) Slog.d(TAG, "refreshAgentList: trustagent " + name
+                            + "of user " + userInfo.id + "can unlock user profile.");
+                }
+
+                if (!StorageManager.isUserKeyUnlocked(userInfo.id)
+                        && !directUnlock) {
+                    if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+                            + "'s trust agent " + name + ": FDE still locked and "
+                            + " the agent cannot unlock user profile.");
+                    continue;
+                }
+
+                if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) {
+                    int flag = mStrongAuthTracker.getStrongAuthForUser(userInfo.id);
+                    if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+                        || !directUnlock) {
+                        if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+                            + ": prevented by StrongAuthTracker = 0x"
+                            + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
+                            userInfo.id)));
+                        continue;
+                    }
+                }
+
+                if (!mActiveAgents.contains(agentInfo)) {
                     mActiveAgents.add(agentInfo);
                 } else {
                     obsoleteAgents.remove(agentInfo);
@@ -468,10 +490,12 @@
         refreshAgentList(userId);
     }
 
-    private ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
+    private SettingsAttrs getSettingsAttrs(PackageManager pm, ResolveInfo resolveInfo) {
         if (resolveInfo == null || resolveInfo.serviceInfo == null
                 || resolveInfo.serviceInfo.metaData == null) return null;
         String cn = null;
+        boolean canUnlockProfile = false;
+
         XmlResourceParser parser = null;
         Exception caughtException = null;
         try {
@@ -496,6 +520,8 @@
             TypedArray sa = res
                     .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
             cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
+            canUnlockProfile = sa.getBoolean(
+                    com.android.internal.R.styleable.TrustAgent_unlockProfile, false);
             sa.recycle();
         } catch (PackageManager.NameNotFoundException e) {
             caughtException = e;
@@ -516,7 +542,7 @@
         if (cn.indexOf('/') < 0) {
             cn = resolveInfo.serviceInfo.packageName + "/" + cn;
         }
-        return ComponentName.unflattenFromString(cn);
+        return new SettingsAttrs(ComponentName.unflattenFromString(cn), canUnlockProfile);
     }
 
     private ComponentName getComponentName(ResolveInfo resolveInfo) {
@@ -554,6 +580,7 @@
 
     private List<ResolveInfo> resolveAllowedTrustAgents(PackageManager pm, int userId) {
         List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
+                PackageManager.GET_META_DATA |
                 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 userId);
         ArrayList<ResolveInfo> allowedAgents = new ArrayList<>(resolveInfos.size());
@@ -991,6 +1018,18 @@
         }
     };
 
+    private static class SettingsAttrs {
+        public ComponentName componentName;
+        public boolean canUnlockProfile;
+
+        public SettingsAttrs(
+                ComponentName componentName,
+                boolean canUnlockProfile) {
+            this.componentName = componentName;
+            this.canUnlockProfile = canUnlockProfile;
+        }
+    };
+
     private class Receiver extends BroadcastReceiver {
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 40b737d..0e4add8 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -68,7 +68,8 @@
  * Drag/drop state
  */
 class DragState {
-    private static final long ANIMATION_DURATION_MS = 500;
+    private static final long MIN_ANIMATION_DURATION_MS = 195;
+    private static final long MAX_ANIMATION_DURATION_MS = 375;
 
     private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ |
             View.DRAG_FLAG_GLOBAL_URI_WRITE;
@@ -103,6 +104,7 @@
     private Animation mAnimation;
     final Transformation mTransformation = new Transformation();
     private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
+    private Point mDisplaySize = new Point();
 
     DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
             int flags, IBinder localWin) {
@@ -171,10 +173,8 @@
             // The drag window covers the entire display
             mDragWindowHandle.frameLeft = 0;
             mDragWindowHandle.frameTop = 0;
-            Point p = new Point();
-            display.getRealSize(p);
-            mDragWindowHandle.frameRight = p.x;
-            mDragWindowHandle.frameBottom = p.y;
+            mDragWindowHandle.frameRight = mDisplaySize.x;
+            mDragWindowHandle.frameBottom = mDisplaySize.y;
 
             // Pause rotations before a drag.
             if (DEBUG_ORIENTATION) {
@@ -215,6 +215,7 @@
      * @param display The Display that the window being dragged is on.
      */
     void register(Display display) {
+        display.getRealSize(mDisplaySize);
         if (DEBUG_DRAG) Slog.d(TAG_WM, "registering drag input channel");
         if (mInputInterceptor != null) {
             Slog.e(TAG_WM, "Duplicate register of drag input channel");
@@ -583,10 +584,17 @@
 
     private Animation createReturnAnimationLocked() {
         final AnimationSet set = new AnimationSet(false);
-        set.addAnimation(new TranslateAnimation(
-                0, mOriginalX - mCurrentX, 0, mOriginalY - mCurrentY));
+        final float translateX = mOriginalX - mCurrentX;
+        final float translateY = mOriginalY - mCurrentY;
+        set.addAnimation(new TranslateAnimation( 0, translateX, 0, translateY));
         set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2));
-        set.setDuration(ANIMATION_DURATION_MS);
+        // Adjust the duration to the travel distance.
+        final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY);
+        final double displayDiagonal =
+                Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
+        final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
+                * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
+        set.setDuration(duration);
         set.setInterpolator(mCubicEaseOutInterpolator);
         set.initialize(0, 0, 0, 0);
         set.start();  // Will start on the first call to getTransformation.
@@ -597,7 +605,7 @@
         final AnimationSet set = new AnimationSet(false);
         set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY));
         set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0));
-        set.setDuration(ANIMATION_DURATION_MS);
+        set.setDuration(MIN_ANIMATION_DURATION_MS);
         set.setInterpolator(mCubicEaseOutInterpolator);
         set.initialize(0, 0, 0, 0);
         set.start();  // Will start on the first call to getTransformation.
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 01a50b7..0ad4e0a 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -135,7 +135,7 @@
         mService = service;
         mDisplayContent = displayContent;
         mSnapAlgorithm = new PipSnapAlgorithm(service.mContext);
-        mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
+        mMotionHelper = new PipMotionHelper(UiThread.getHandler());
         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
         reloadResources();
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 29afc8d..612af75 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -19,7 +19,9 @@
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -280,6 +282,17 @@
         return ActivityInfo.isResizeableMode(mResizeMode) || mService.mForceResizableTasks;
     }
 
+    /**
+     * Tests if the orientation should be preserved upon user interactive resizig operations.
+
+     * @return true if orientation should not get changed upon resizing operation.
+     */
+    boolean preserveOrientationOnResize() {
+        return mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
+                || mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
+                || mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+    }
+
     boolean isOnTopLauncher() {
         return mIsOnTopLauncher;
     }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 6887312..267566b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -50,9 +50,9 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.MotionEvent;
-import android.view.SurfaceControl;
 import android.view.WindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.input.InputWindowHandle;
 import com.android.server.wm.WindowManagerService.H;
@@ -61,6 +61,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 class TaskPositioner implements DimLayer.DimLayerUser {
+    private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
     private static final String TAG_LOCAL = "TaskPositioner";
     private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
 
@@ -89,6 +90,12 @@
 
     public static final int RESIZING_HINT_DURATION_MS = 0;
 
+    // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
+    // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
+    // aspect he desires.
+    @VisibleForTesting
+    static final float MIN_ASPECT = 1.2f;
+
     private final WindowManagerService mService;
     private WindowPositionerEventReceiver mInputEventReceiver;
     private Display mDisplay;
@@ -103,8 +110,11 @@
 
     private Task mTask;
     private boolean mResizing;
+    private boolean mPreserveOrientation;
+    private boolean mStartOrientationWasLandscape;
     private final Rect mWindowOriginalBounds = new Rect();
     private final Rect mWindowDragBounds = new Rect();
+    private final Point mMaxVisibleSize = new Point();
     private float mStartDragX;
     private float mStartDragY;
     @CtrlType
@@ -226,6 +236,11 @@
         mService = service;
     }
 
+    @VisibleForTesting
+    Rect getWindowDragBounds() {
+        return mWindowDragBounds;
+    }
+
     /**
      * @param display The Display that the window being dragged is on.
      */
@@ -294,6 +309,7 @@
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+        mDisplay.getRealSize(mMaxVisibleSize);
 
         mDragEnded = false;
     }
@@ -335,44 +351,57 @@
         mService.resumeRotationLocked();
     }
 
-    void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
+    void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
+                   float startY) {
         if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
-                + ", {" + startX + ", " + startY + "}");
+            Slog.d(TAG, "startDrag: win=" + win + ", resize=" + resize
+                    + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
+                    + startY + "}");
         }
-        mCtrlType = CTRL_NONE;
         mTask = win.getTask();
-        mStartDragX = startX;
-        mStartDragY = startY;
-
         // Use the dim bounds, not the original task bounds. The cursor
         // movement should be calculated relative to the visible bounds.
         // Also, use the dim bounds of the task which accounts for
         // multiple app windows. Don't use any bounds from win itself as it
         // may not be the same size as the task.
         mTask.getDimBounds(mTmpRect);
+        startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
+    }
+
+    @VisibleForTesting
+    void startDrag(boolean resize, boolean preserveOrientation,
+                   float startX, float startY, Rect startBounds) {
+        mCtrlType = CTRL_NONE;
+        mStartDragX = startX;
+        mStartDragY = startY;
+        mPreserveOrientation = preserveOrientation;
 
         if (resize) {
-            if (startX < mTmpRect.left) {
+            if (startX < startBounds.left) {
                 mCtrlType |= CTRL_LEFT;
             }
-            if (startX > mTmpRect.right) {
+            if (startX > startBounds.right) {
                 mCtrlType |= CTRL_RIGHT;
             }
-            if (startY < mTmpRect.top) {
+            if (startY < startBounds.top) {
                 mCtrlType |= CTRL_TOP;
             }
-            if (startY > mTmpRect.bottom) {
+            if (startY > startBounds.bottom) {
                 mCtrlType |= CTRL_BOTTOM;
             }
-            mResizing = true;
+            mResizing = mCtrlType != CTRL_NONE;
         }
 
-        mWindowOriginalBounds.set(mTmpRect);
+        // In case of !isDockedInEffect we are using the union of all task bounds. These might be
+        // made up out of multiple windows which are only partially overlapping. When that happens,
+        // the orientation from the window of interest to the entire stack might diverge. However
+        // for now we treat them as the same.
+        mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
+        mWindowOriginalBounds.set(startBounds);
 
         // Make sure we always have valid drag bounds even if the drag ends before any move events
         // have been handled.
-        mWindowDragBounds.set(mTmpRect);
+        mWindowDragBounds.set(startBounds);
     }
 
     private void endDragLocked() {
@@ -387,26 +416,7 @@
         }
 
         if (mCtrlType != CTRL_NONE) {
-            // This is a resizing operation.
-            final int deltaX = Math.round(x - mStartDragX);
-            final int deltaY = Math.round(y - mStartDragY);
-            int left = mWindowOriginalBounds.left;
-            int top = mWindowOriginalBounds.top;
-            int right = mWindowOriginalBounds.right;
-            int bottom = mWindowOriginalBounds.bottom;
-            if ((mCtrlType & CTRL_LEFT) != 0) {
-                left = Math.min(left + deltaX, right - mMinVisibleWidth);
-            }
-            if ((mCtrlType & CTRL_TOP) != 0) {
-                top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
-            }
-            if ((mCtrlType & CTRL_RIGHT) != 0) {
-                right = Math.max(left + mMinVisibleWidth, right + deltaX);
-            }
-            if ((mCtrlType & CTRL_BOTTOM) != 0) {
-                bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
-            }
-            mWindowDragBounds.set(left, top, right, bottom);
+            resizeDrag(x, y);
             mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
             return false;
         }
@@ -428,6 +438,168 @@
         return false;
     }
 
+    /**
+     * The user is drag - resizing the window.
+     *
+     * @param x The x coordinate of the current drag coordinate.
+     * @param y the y coordinate of the current drag coordinate.
+     */
+    @VisibleForTesting
+    void resizeDrag(float x, float y) {
+        // This is a resizing operation.
+        // We need to keep various constraints:
+        // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
+        // 2. The orientation is kept - if required.
+        final int deltaX = Math.round(x - mStartDragX);
+        final int deltaY = Math.round(y - mStartDragY);
+        int left = mWindowOriginalBounds.left;
+        int top = mWindowOriginalBounds.top;
+        int right = mWindowOriginalBounds.right;
+        int bottom = mWindowOriginalBounds.bottom;
+
+        // The aspect which we have to respect. Note that if the orientation does not need to be
+        // preserved the aspect will be calculated as 1.0 which neutralizes the following
+        // computations.
+        final float minAspect = !mPreserveOrientation
+                ? 1.0f
+                : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
+        // Calculate the resulting width and height of the drag operation.
+        int width = right - left;
+        int height = bottom - top;
+        if ((mCtrlType & CTRL_LEFT) != 0) {
+            width = Math.max(mMinVisibleWidth, width - deltaX);
+        } else if ((mCtrlType & CTRL_RIGHT) != 0) {
+            width = Math.max(mMinVisibleWidth, width + deltaX);
+        }
+        if ((mCtrlType & CTRL_TOP) != 0) {
+            height = Math.max(mMinVisibleHeight, height - deltaY);
+        } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
+            height = Math.max(mMinVisibleHeight, height + deltaY);
+        }
+
+        // If we have to preserve the orientation - check that we are doing so.
+        final float aspect = (float) width / (float) height;
+        if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
+                || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
+            // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
+            // drag axis. What ever is producing the bigger rectangle will be chosen.
+            int width1;
+            int width2;
+            int height1;
+            int height2;
+            if (mStartOrientationWasLandscape) {
+                // Assuming that the width is our target we calculate the height.
+                width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
+                height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
+                if (height1 < mMinVisibleHeight) {
+                    // If the resulting height is too small we adjust to the minimal size.
+                    height1 = mMinVisibleHeight;
+                    width1 = Math.max(mMinVisibleWidth,
+                            Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
+                }
+                // Assuming that the height is our target we calculate the width.
+                height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
+                width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
+                if (width2 < mMinVisibleWidth) {
+                    // If the resulting width is too small we adjust to the minimal size.
+                    width2 = mMinVisibleWidth;
+                    height2 = Math.max(mMinVisibleHeight,
+                            Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
+                }
+            } else {
+                // Assuming that the width is our target we calculate the height.
+                width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
+                height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
+                if (height1 < mMinVisibleHeight) {
+                    // If the resulting height is too small we adjust to the minimal size.
+                    height1 = mMinVisibleHeight;
+                    width1 = Math.max(mMinVisibleWidth,
+                            Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
+                }
+                // Assuming that the height is our target we calculate the width.
+                height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
+                width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
+                if (width2 < mMinVisibleWidth) {
+                    // If the resulting width is too small we adjust to the minimal size.
+                    width2 = mMinVisibleWidth;
+                    height2 = Math.max(mMinVisibleHeight,
+                            Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
+                }
+            }
+
+            // Use the bigger of the two rectangles if the major change was positive, otherwise
+            // do the opposite.
+            final boolean grows = width > (right - left) || height > (bottom - top);
+            if (grows == (width1 * height1 > width2 * height2)) {
+                width = width1;
+                height = height1;
+            } else {
+                width = width2;
+                height = height2;
+            }
+        }
+
+        // Update mWindowDragBounds to the new drag size.
+        updateDraggedBounds(left, top, right, bottom, width, height);
+    }
+
+    /**
+     * Given the old coordinates and the new width and height, update the mWindowDragBounds.
+     *
+     * @param left      The original left bound before the user started dragging.
+     * @param top       The original top bound before the user started dragging.
+     * @param right     The original right bound before the user started dragging.
+     * @param bottom    The original bottom bound before the user started dragging.
+     * @param newWidth  The new dragged width.
+     * @param newHeight The new dragged height.
+     */
+    void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
+                             int newHeight) {
+        // Generate the final bounds by keeping the opposite drag edge constant.
+        if ((mCtrlType & CTRL_LEFT) != 0) {
+            left = right - newWidth;
+        } else { // Note: The right might have changed - if we pulled at the right or not.
+            right = left + newWidth;
+        }
+        if ((mCtrlType & CTRL_TOP) != 0) {
+            top = bottom - newHeight;
+        } else { // Note: The height might have changed - if we pulled at the bottom or not.
+            bottom = top + newHeight;
+        }
+
+        mWindowDragBounds.set(left, top, right, bottom);
+
+        checkBoundsForOrientationViolations(mWindowDragBounds);
+    }
+
+    /**
+     * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
+     *
+     * @param bounds The bounds to be checked.
+     */
+    private void checkBoundsForOrientationViolations(Rect bounds) {
+        // When using debug check that we are not violating the given constraints.
+        if (DEBUG_ORIENTATION_VIOLATIONS) {
+            if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
+                Slog.e(TAG, "Orientation violation detected! should be "
+                        + (mStartOrientationWasLandscape ? "landscape" : "portrait")
+                        + " but is the other");
+            } else {
+                Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
+            }
+            if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
+                Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
+                        + ", " + bounds.width() + ") Height(min,is)=("
+                        + mMinVisibleHeight + ", " + bounds.height() + ")");
+            }
+            if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
+                Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
+                        + ", " + bounds.width() + ") Height(min,is)=("
+                        + mMaxVisibleSize.y + ", " + bounds.height() + ")");
+            }
+        }
+    }
+
     private void updateWindowDragBounds(int x, int y, Rect stackBounds) {
         final int offsetX = Math.round(x - mStartDragX);
         final int offsetY = Math.round(y - mStartDragY);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 04cf2e3..e68960c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3602,6 +3602,11 @@
     }
 
     @Override
+    public WindowManagerPolicy.WindowState getInputMethodWindowLw() {
+        return mInputMethodWindow;
+    }
+
+    @Override
     public void notifyKeyguardTrustedChanged() {
         mH.sendEmptyMessage(H.NOTIFY_KEYGUARD_TRUSTED_CHANGED);
     }
@@ -5726,7 +5731,8 @@
             win = windowForClientLocked(null, window, false);
             // win shouldn't be null here, pass it down to startPositioningLocked
             // to get warning if it's null.
-            if (!startPositioningLocked(win, false /*resize*/, startX, startY)) {
+            if (!startPositioningLocked(
+                        win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
                 return false;
             }
         }
@@ -5741,8 +5747,8 @@
         synchronized (mWindowMap) {
             final Task task = displayContent.findTaskForResizePoint(x, y);
             if (task != null) {
-                if (!startPositioningLocked(
-                        task.getTopVisibleAppMainWindow(), true /*resize*/, x, y)) {
+                if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
+                            task.preserveOrientationOnResize(), x, y)) {
                     return;
                 }
                 taskId = task.mTaskId;
@@ -5757,10 +5763,12 @@
         }
     }
 
-    private boolean startPositioningLocked(
-            WindowState win, boolean resize, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "startPositioningLocked: "
-            + "win=" + win + ", resize=" + resize + ", {" + startX + ", " + startY + "}");
+    private boolean startPositioningLocked(WindowState win, boolean resize,
+            boolean preserveOrientation, float startX, float startY) {
+        if (DEBUG_TASK_POSITIONING)
+            Slog.d(TAG_WM, "startPositioningLocked: "
+                            + "win=" + win + ", resize=" + resize + ", preserveOrientation="
+                            + preserveOrientation + ", {" + startX + ", " + startY + "}");
 
         if (win == null || win.getAppToken() == null) {
             Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
@@ -5801,7 +5809,7 @@
             return false;
         }
 
-        mTaskPositioner.startDragLocked(win, resize, startX, startY);
+        mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4b8c4bb..e2027fd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1925,9 +1925,17 @@
     }
 
     boolean canBeImeTarget() {
-        final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
+        if (mIsImWindow) {
+            // IME windows can't be IME targets. IME targets are required to be below the IME
+            // windows and that wouldn't be possible if the IME window is its own target...silly.
+            return false;
+        }
+
+        final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
         final int type = mAttrs.type;
 
+        // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are set or
+        // both are cleared...and not a starting window.
         if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
                 && type != TYPE_APPLICATION_STARTING) {
             return false;
@@ -2875,7 +2883,7 @@
         mShowToOwnerOnly = showToOwnerOnly;
     }
 
-    boolean isHiddenFromUserLocked() {
+    private boolean isHiddenFromUserLocked() {
         // Child windows are evaluated based on their parent window.
         final WindowState win = getTopParentWindow();
         if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
@@ -3544,16 +3552,23 @@
 
     /** Returns the topmost parent window if this is a child of another window, else this. */
     WindowState getTopParentWindow() {
-        WindowState w = this;
-        while (w != null && w.mIsChildWindow) {
-            w = w.getParentWindow();
+        WindowState current = this;
+        WindowState topParent = current;
+        while (current != null && current.mIsChildWindow) {
+            current = current.getParentWindow();
+            // Parent window can be null if the child is detached from it's parent already, but
+            // someone still has a reference to access it. So, we return the top parent value we
+            // already have instead of null.
+            if (current != null) {
+                topParent = current;
+            }
         }
-        return w;
+        return topParent;
     }
 
     boolean isParentWindowHidden() {
         final WindowState parent = getParentWindow();
-        return (parent == null) ? false : parent.mHidden;
+        return parent != null && parent.mHidden;
     }
 
     void setWillReplaceWindow(boolean animate) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 45f3698..aafc432 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3093,6 +3093,7 @@
                 // If admin is a device or profile owner tidy that up first.
                 if (isDeviceOwner(adminReceiver, userHandle)) {
                     clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
+                    clearDeviceOwnerUserRestrictionLocked(UserHandle.of(userHandle));
                 }
                 if (isProfileOwner(adminReceiver, userHandle)) {
                     final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
@@ -3108,6 +3109,15 @@
         }
     }
 
+    // It's temporary solution to clear DISALLOW_ADD_USER after CTS
+    // TODO: b/31952368 when the restriction is moved from system to the device owner,
+    // it can be removed.
+    private void clearDeviceOwnerUserRestrictionLocked(UserHandle userHandle) {
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userHandle);
+        }
+    }
+
     /**
      * Return if a given package has testOnly="true", in which case we'll relax certain rules
      * for CTS.
@@ -9856,9 +9866,8 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(admin);
         synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            enforceDeviceOwnerOrManageUsers();
             return isNetworkLoggingEnabledInternalLocked();
         }
     }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
new file mode 100644
index 0000000..b6166f6
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.NotificationChannel;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationRankingUpdate;
+import android.service.notification.SnoozeCriterion;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationListenerServiceTest {
+
+    private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
+
+    @Test
+    public void testRanking() throws Exception {
+        TestListenerService service = new TestListenerService();
+        service.applyUpdateLocked(generateUpdate());
+        for (int i = 0; i < mKeys.length; i++) {
+            String key = mKeys[i];
+            Ranking ranking = new Ranking();
+            service.getCurrentRanking().getRanking(key, ranking);
+            assertEquals(getVisibilityOverride(i), ranking.getVisibilityOverride());
+            assertEquals(getOverrideGroupKey(key), ranking.getOverrideGroupKey());
+            assertEquals(!isIntercepted(i), ranking.matchesInterruptionFilter());
+            assertEquals(getSuppressedVisualEffects(i), ranking.getSuppressedVisualEffects());
+            assertEquals(getImportance(i), ranking.getImportance());
+            assertEquals(getExplanation(key), ranking.getImportanceExplanation());
+            assertEquals(getChannel(key, i), ranking.getChannel());
+            assertEquals(getPeople(key, i), ranking.getAdditionalPeople());
+            assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
+        }
+    }
+
+    private NotificationRankingUpdate generateUpdate() {
+        List<String> interceptedKeys = new ArrayList<>();
+        Bundle visibilityOverrides = new Bundle();
+        Bundle overrideGroupKeys = new Bundle();
+        Bundle suppressedVisualEffects = new Bundle();
+        Bundle explanation = new Bundle();
+        Bundle overrideChannels = new Bundle();
+        Bundle overridePeople = new Bundle();
+        Bundle snoozeCriteria = new Bundle();
+        int[] importance = new int[mKeys.length];
+
+        for (int i = 0; i < mKeys.length; i++) {
+            String key = mKeys[i];
+            visibilityOverrides.putInt(key, getVisibilityOverride(i));
+            overrideGroupKeys.putString(key, getOverrideGroupKey(key));
+            if (isIntercepted(i)) {
+                interceptedKeys.add(key);
+            }
+            suppressedVisualEffects.putInt(key, getSuppressedVisualEffects(i));
+            importance[i] = getImportance(i);
+            explanation.putString(key, getExplanation(key));
+            overrideChannels.putParcelable(key, getChannel(key, i));
+            overridePeople.putStringArrayList(key, getPeople(key, i));
+            snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
+        }
+        NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
+                interceptedKeys.toArray(new String[0]), visibilityOverrides,
+                suppressedVisualEffects, importance, explanation, overrideGroupKeys,
+                overrideChannels, overridePeople, snoozeCriteria);
+        return update;
+    }
+
+    private int getVisibilityOverride(int index) {
+        return index * 9;
+    }
+
+    private String getOverrideGroupKey(String key) {
+        return key + key;
+    }
+
+    private boolean isIntercepted(int index) {
+        return index % 2 == 0;
+    }
+
+    private int getSuppressedVisualEffects(int index) {
+        return index * 2;
+    }
+
+    private int getImportance(int index) {
+        return index;
+    }
+
+    private String getExplanation(String key) {
+        return key + "explain";
+    }
+
+    private NotificationChannel getChannel(String key, int index) {
+        return new NotificationChannel(key, key, getImportance(index));
+    }
+
+    private ArrayList<String> getPeople(String key, int index) {
+        ArrayList<String> people = new ArrayList<>();
+        for (int i = 0; i < index; i++) {
+            people.add(i + key);
+        }
+        return people;
+    }
+
+    private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key, int index) {
+        ArrayList<SnoozeCriterion> snooze = new ArrayList<>();
+        for (int i = 0; i < index; i++) {
+            snooze.add(new SnoozeCriterion(key + i, getExplanation(key), key));
+        }
+        return snooze;
+    }
+
+    public static class TestListenerService extends NotificationListenerService {
+        private final IBinder binder = new LocalBinder();
+
+        public TestListenerService() {
+
+        }
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            super.onBind(intent);
+            return binder;
+        }
+
+        public class LocalBinder extends Binder {
+            TestListenerService getService() {
+                return TestListenerService.this;
+            }
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
new file mode 100644
index 0000000..50911cb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2012 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;
+
+import static android.net.NetworkScoreManager.CACHE_FILTER_NONE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.Manifest.permission;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.INetworkScoreCache;
+import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
+import android.net.NetworkScorerAppManager;
+import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.net.ScoredNetwork;
+import android.net.WifiKey;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.server.devicepolicy.MockUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link NetworkScoreService}.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class NetworkScoreServiceTest {
+    private static final ScoredNetwork SCORED_NETWORK =
+            new ScoredNetwork(new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
+                    null /* rssiCurve*/);
+    private static final NetworkScorerAppData PREV_SCORER = new NetworkScorerAppData(
+            "prevPackageName", 0, "prevScorerName", null /* configurationActivityClassName */,
+            "prevScoringServiceClass");
+    private static final NetworkScorerAppData NEW_SCORER = new NetworkScorerAppData(
+            "newPackageName", 1, "newScorerName", null /* configurationActivityClassName */,
+            "newScoringServiceClass");
+
+    @Mock private PackageManager mPackageManager;
+    @Mock private NetworkScorerAppManager mNetworkScorerAppManager;
+    @Mock private Context mContext;
+    @Mock private Resources mResources;
+    @Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
+    @Mock private IBinder mIBinder, mIBinder2;
+    @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
+
+    private ContentResolver mContentResolver;
+    private NetworkScoreService mNetworkScoreService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mNetworkScoreCache.asBinder()).thenReturn(mIBinder);
+        when(mNetworkScoreCache2.asBinder()).thenReturn(mIBinder2);
+        mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.getResources()).thenReturn(mResources);
+        mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
+    }
+
+    @Test
+    public void testSystemReady_networkScorerProvisioned() throws Exception {
+        Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 1);
+
+        mNetworkScoreService.systemReady();
+
+        verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
+    }
+
+    @Test
+    public void testSystemReady_networkScorerNotProvisioned_defaultScorer() throws Exception {
+        Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
+
+        when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
+                .thenReturn(NEW_SCORER.mPackageName);
+
+        mNetworkScoreService.systemReady();
+
+        verify(mNetworkScorerAppManager).setActiveScorer(NEW_SCORER.mPackageName);
+        assertEquals(1,
+                Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
+
+    }
+
+    @Test
+    public void testSystemReady_networkScorerNotProvisioned_noDefaultScorer() throws Exception {
+        Settings.Global.putInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED, 0);
+
+        when(mResources.getString(R.string.config_defaultNetworkScorerPackageName))
+                .thenReturn(null);
+
+        mNetworkScoreService.systemReady();
+
+        verify(mNetworkScorerAppManager, never()).setActiveScorer(anyString());
+        assertEquals(1,
+                Settings.Global.getInt(mContentResolver, Global.NETWORK_SCORING_PROVISIONED));
+    }
+
+    @Test
+    public void testSystemRunning() {
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
+
+        mNetworkScoreService.systemRunning();
+
+        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
+                new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
+                any(ServiceConnection.class),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                eq(UserHandle.SYSTEM));
+    }
+
+    @Test
+    public void testUpdateScores_notActiveScorer() {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+
+        try {
+            mNetworkScoreService.updateScores(new ScoredNetwork[0]);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testUpdateScores_oneRegisteredCache() throws RemoteException {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
+                mNetworkScoreCache, CACHE_FILTER_NONE);
+
+        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
+
+        verify(mNetworkScoreCache).updateScores(mScoredNetworkCaptor.capture());
+
+        assertEquals(1, mScoredNetworkCaptor.getValue().size());
+        assertEquals(SCORED_NETWORK, mScoredNetworkCaptor.getValue().get(0));
+    }
+
+    @Test
+    public void testUpdateScores_twoRegisteredCaches() throws RemoteException {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
+                mNetworkScoreCache, CACHE_FILTER_NONE);
+        mNetworkScoreService.registerNetworkScoreCache(
+                NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE);
+
+        // updateScores should update both caches
+        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
+
+        verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class));
+        verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class));
+
+        mNetworkScoreService.unregisterNetworkScoreCache(
+                NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
+
+        // updateScores should only update the first cache since the 2nd has been unregistered
+        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
+
+        verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class));
+
+        mNetworkScoreService.unregisterNetworkScoreCache(
+                NetworkKey.TYPE_WIFI, mNetworkScoreCache);
+
+        // updateScores should not update any caches since they are both unregistered
+        mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
+
+        // The register and unregister calls grab the binder from the score cache.
+        verify(mNetworkScoreCache, atLeastOnce()).asBinder();
+        verify(mNetworkScoreCache2, atLeastOnce()).asBinder();
+        verifyNoMoreInteractions(mNetworkScoreCache, mNetworkScoreCache2);
+    }
+
+    @Test
+    public void testClearScores_notActiveScorer_noBroadcastNetworkPermission() {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+            .thenReturn(PackageManager.PERMISSION_DENIED);
+        try {
+            mNetworkScoreService.clearScores();
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testClearScores_activeScorer_noBroadcastNetworkPermission() {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+            .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        mNetworkScoreService.clearScores();
+    }
+
+    @Test
+    public void testClearScores_activeScorer() throws RemoteException {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
+                CACHE_FILTER_NONE);
+        mNetworkScoreService.clearScores();
+
+        verify(mNetworkScoreCache).clearScores();
+    }
+
+    @Test
+    public void testClearScores_notActiveScorer_hasBroadcastNetworkPermission()
+            throws RemoteException {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
+                CACHE_FILTER_NONE);
+        mNetworkScoreService.clearScores();
+
+        verify(mNetworkScoreCache).clearScores();
+    }
+
+    @Test
+    public void testSetActiveScorer_noScoreNetworksPermission() {
+        doThrow(new SecurityException()).when(mContext)
+                .enforceCallingOrSelfPermission(eq(permission.SCORE_NETWORKS), anyString());
+
+        try {
+            mNetworkScoreService.setActiveScorer(null);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testSetActiveScorer_failure() throws RemoteException {
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER);
+        when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(false);
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
+                CACHE_FILTER_NONE);
+
+        boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
+
+        assertFalse(success);
+        verify(mNetworkScoreCache).clearScores();
+        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
+                new ComponentName(PREV_SCORER.mPackageName, PREV_SCORER.mScoringServiceClassName))),
+                any(ServiceConnection.class),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                eq(UserHandle.SYSTEM));
+    }
+
+    @Test
+    public void testSetActiveScorer_success() throws RemoteException {
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, NEW_SCORER);
+        when(mNetworkScorerAppManager.setActiveScorer(NEW_SCORER.mPackageName)).thenReturn(true);
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
+                CACHE_FILTER_NONE);
+
+        boolean success = mNetworkScoreService.setActiveScorer(NEW_SCORER.mPackageName);
+
+        assertTrue(success);
+        verify(mNetworkScoreCache).clearScores();
+        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
+                new ComponentName(NEW_SCORER.mPackageName, NEW_SCORER.mScoringServiceClassName))),
+                any(ServiceConnection.class),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                eq(UserHandle.SYSTEM));
+        verify(mContext, times(2)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(NetworkScoreManager.ACTION_SCORER_CHANGED),
+                eq(UserHandle.SYSTEM));
+    }
+
+    @Test
+    public void testDisableScoring_notActiveScorer_noBroadcastNetworkPermission() {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        try {
+            mNetworkScoreService.disableScoring();
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testDisableScoring_activeScorer() throws RemoteException {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true);
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
+        when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
+                CACHE_FILTER_NONE);
+
+        mNetworkScoreService.disableScoring();
+
+        verify(mNetworkScoreCache).clearScores();
+        verify(mContext).sendBroadcastAsUser(
+                MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
+                        .setPackage(PREV_SCORER.mPackageName)),
+                eq(UserHandle.SYSTEM));
+        verify(mContext, never()).bindServiceAsUser(any(Intent.class),
+                any(ServiceConnection.class), anyInt(), any(UserHandle.class));
+    }
+
+    @Test
+    public void testDisableScoring_notActiveScorer_hasBroadcastNetworkPermission()
+            throws RemoteException {
+        when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false);
+        when(mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(PREV_SCORER, null);
+        when(mNetworkScorerAppManager.setActiveScorer(null)).thenReturn(true);
+        mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
+                CACHE_FILTER_NONE);
+
+        mNetworkScoreService.disableScoring();
+
+        verify(mNetworkScoreCache).clearScores();
+        verify(mContext).sendBroadcastAsUser(
+                MockUtils.checkIntent(new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED)
+                        .setPackage(PREV_SCORER.mPackageName)),
+                eq(UserHandle.SYSTEM));
+        verify(mContext, never()).bindServiceAsUser(any(Intent.class),
+                any(ServiceConnection.class), anyInt(), any(UserHandle.class));
+    }
+
+    @Test
+    public void testRegisterNetworkScoreCache_noBroadcastNetworkPermission() {
+        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
+
+        try {
+            mNetworkScoreService.registerNetworkScoreCache(
+                NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testUnregisterNetworkScoreCache_noBroadcastNetworkPermission() {
+        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+                eq(permission.BROADCAST_NETWORK_PRIVILEGED), anyString());
+
+        try {
+            mNetworkScoreService.unregisterNetworkScoreCache(
+                    NetworkKey.TYPE_WIFI, mNetworkScoreCache);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testDump_noDumpPermission() {
+        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+                eq(permission.DUMP), anyString());
+
+        try {
+            mNetworkScoreService.dump(
+                    new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testDump_doesNotCrash() {
+        when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
+        StringWriter stringWriter = new StringWriter();
+
+        mNetworkScoreService.dump(
+                new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+
+        assertFalse(stringWriter.toString().isEmpty());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 9c241d7..c74cda6 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -254,11 +254,11 @@
 
         // Unlock the user and verify that db has been updated
         ams2.onUserUnlocked(newIntentForUser(UserHandle.USER_SYSTEM));
-        accountsNumber = readNumberOfAccountsFromDbFile(originalContext, ceDatabaseName);
-        assertEquals("CE database should now have 1 account", 2, accountsNumber);
         accounts = ams2.getAccounts(UserHandle.USER_SYSTEM, mContext.getOpPackageName());
         assertEquals(1, accounts.length);
         assertEquals("Only a2 should be returned", a2, accounts[0]);
+        accountsNumber = readNumberOfAccountsFromDbFile(originalContext, ceDatabaseName);
+        assertEquals("CE database should now have 1 account", 1, accountsNumber);
     }
 
     @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 58db192..3806da6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -82,6 +82,21 @@
         return Mockito.argThat(m);
     }
 
+    public static Intent checkIntent(final Intent intent) {
+        final Matcher<Intent> m = new BaseMatcher<Intent>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) return false;
+                return intent.filterEquals((Intent) item);
+            }
+            @Override
+            public void describeTo(Description description) {
+                description.appendText(intent.toString());
+            }
+        };
+        return Mockito.argThat(m);
+    }
+
     public static Bundle checkUserRestrictions(String... keys) {
         final Bundle expected = DpmTestUtils.newRestrictions(Preconditions.checkNotNull(keys));
         final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
index 1188bb7..5c552a2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
@@ -100,16 +100,17 @@
             if ((protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
                 boolean granted = (packageInfo.requestedPermissionsFlags[i]
                         & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
-                assertTrue("Permission " + pName + " should be granted to " + packageName, granted);
                 // if privapp permissions are enforced, platform permissions must be whitelisted
                 // in SystemConfig
                 if (platformPermission && RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
                     assertTrue("Permission " + pName
-                                    + " should be declared in privapp-permissions-platform.xml "
-                                    + "or privapp-permissions-<product>.xml file for package "
+                                    + " should be declared in "
+                                    + "/etc/permissions/privapp-permissions-platform.xml "
+                                    + "or privapp-permissions-<device>.xml file for package "
                                     + packageName,
                             privAppPermissions.contains(pName));
                 }
+                assertTrue("Permission " + pName + " should be granted to " + packageName, granted);
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
new file mode 100644
index 0000000..1260a53
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.graphics.Rect;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link TaskPositioner} class.
+ *
+ * runtest frameworks-services -c com.android.server.wm.TaskPositionerTests
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskPositionerTests extends WindowTestsBase {
+
+    private final boolean DEBUGGING = false;
+    private final String TAG = "TaskPositionerTest";
+
+    private final static int MOUSE_DELTA_X = 5;
+    private final static int MOUSE_DELTA_Y = 5;
+
+    private int mMinVisibleWidth;
+    private int mMinVisibleHeight;
+    private TaskPositioner mPositioner;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        final Display display = sDisplayContent.getDisplay();
+        final DisplayMetrics dm = new DisplayMetrics();
+        display.getMetrics(dm);
+
+        // This should be the same calculation as the TaskPositioner uses.
+        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
+        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
+
+        mPositioner = new TaskPositioner(sWm);
+        mPositioner.register(display);
+    }
+
+    /**
+     * This tests that free resizing will allow to change the orientation as well
+     * as does some basic tests (e.g. dragging in Y only will keep X stable).
+     */
+    @Test
+    public void testBasicFreeWindowResizing() throws Exception {
+        final Rect r = new Rect(100, 220, 700, 520);
+        final int midY = (r.top + r.bottom) / 2;
+
+        // Start a drag resize starting upper left.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
+        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
+
+        // Drag to a good landscape size.
+        mPositioner.resizeDrag(0.0f, 0.0f);
+        assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a good portrait size.
+        mPositioner.resizeDrag(400.0f, 0.0f);
+        assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a too small size for the width.
+        mPositioner.resizeDrag(2000.0f, r.top);
+        assertBoundsEquals(
+                new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a too small size for the height.
+        mPositioner.resizeDrag(r.left, 2000.0f);
+        assertBoundsEquals(
+                new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Start a drag resize left and see that only the left coord changes..
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY, r);
+
+        // Drag to the left.
+        mPositioner.resizeDrag(0.0f, midY);
+        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the right.
+        mPositioner.resizeDrag(200.0f, midY);
+        assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the top
+        mPositioner.resizeDrag(r.left, 0.0f);
+        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the bottom
+        mPositioner.resizeDrag(r.left, 1000.0f);
+        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    /**
+     * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
+     */
+    @Test
+    public void testFreeWindowResizingTestAllEdges() throws Exception {
+        final Rect r = new Rect(100, 220, 700, 520);
+        final int midX = (r.left + r.right) / 2;
+        final int midY = (r.top + r.bottom) / 2;
+
+        // Drag upper left.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
+        mPositioner.resizeDrag(0.0f, 0.0f);
+        assertTrue(r.left != mPositioner.getWindowDragBounds().left);
+        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
+        assertTrue(r.top != mPositioner.getWindowDragBounds().top);
+        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
+
+        // Drag upper.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y, r);
+        mPositioner.resizeDrag(0.0f, 0.0f);
+        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
+        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
+        assertTrue(r.top != mPositioner.getWindowDragBounds().top);
+        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
+
+        // Drag upper right.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
+        mPositioner.resizeDrag(r.right + 100, 0.0f);
+        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
+        assertTrue(r.right != mPositioner.getWindowDragBounds().right);
+        assertTrue(r.top != mPositioner.getWindowDragBounds().top);
+        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
+
+        // Drag right.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, midY, r);
+        mPositioner.resizeDrag(r.right + 100, 0.0f);
+        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
+        assertTrue(r.right != mPositioner.getWindowDragBounds().right);
+        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
+        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
+
+        // Drag bottom right.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/,
+                r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y, r);
+        mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
+        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
+        assertTrue(r.right != mPositioner.getWindowDragBounds().right);
+        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
+        assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom);
+
+        // Drag bottom.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, midX, r.bottom + MOUSE_DELTA_Y, r);
+        mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
+        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
+        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
+        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
+        assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom);
+
+        // Drag bottom left.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y, r);
+        mPositioner.resizeDrag(0.0f, r.bottom + 100);
+        assertTrue(r.left != mPositioner.getWindowDragBounds().left);
+        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
+        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
+        assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom);
+
+        // Drag left.
+        mPositioner.startDrag(true /*resizing*/,
+                false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midX, r);
+        mPositioner.resizeDrag(0.0f, r.bottom + 100);
+        assertTrue(r.left != mPositioner.getWindowDragBounds().left);
+        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
+        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
+        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
+    }
+
+    /**
+     * This tests that a constrained landscape window will keep the aspect and do the
+     * right things upon resizing when dragged from the top left corner.
+     */
+    @Test
+    public void testLandscapePreservedWindowResizingDragTopLeft() throws Exception {
+        final Rect r = new Rect(100, 220, 700, 520);
+
+        mPositioner.startDrag(true /*resizing*/,
+                true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
+        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
+
+        // Drag to a good landscape size.
+        mPositioner.resizeDrag(0.0f, 0.0f);
+        assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a good portrait size.
+        mPositioner.resizeDrag(400.0f, 0.0f);
+        int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT);
+        assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a too small size for the width.
+        mPositioner.resizeDrag(2000.0f, r.top);
+        final int w = mMinVisibleWidth;
+        final int h = Math.round(w / MIN_ASPECT);
+        assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a too small size for the height.
+        mPositioner.resizeDrag(r.left, 2000.0f);
+        assertBoundsEquals(
+                new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    /**
+     * This tests that a constrained landscape window will keep the aspect and do the
+     * right things upon resizing when dragged from the left corner.
+     */
+    @Test
+    public void testLandscapePreservedWindowResizingDragLeft() throws Exception {
+        final Rect r = new Rect(100, 220, 700, 520);
+        final int midY = (r.top + r.bottom) / 2;
+
+        mPositioner.startDrag(true /*resizing*/,
+                true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY, r);
+
+        // Drag to the left.
+        mPositioner.resizeDrag(0.0f, midY);
+        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the right.
+        mPositioner.resizeDrag(200.0f, midY);
+        assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag all the way to the right and see the height also shrinking.
+        mPositioner.resizeDrag(2000.0f, midY);
+        final int w = mMinVisibleWidth;
+        final int h = Math.round((float)w / MIN_ASPECT);
+        assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the top.
+        mPositioner.resizeDrag(r.left, 0.0f);
+        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the bottom.
+        mPositioner.resizeDrag(r.left, 1000.0f);
+        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    /**
+     * This tests that a constrained landscape window will keep the aspect and do the
+     * right things upon resizing when dragged from the top corner.
+     */
+    @Test
+    public void testLandscapePreservedWindowResizingDragTop() throws Exception {
+        final Rect r = new Rect(100, 220, 700, 520);
+        final int midX = (r.left + r.right) / 2;
+
+        mPositioner.startDrag(true /*resizing*/,
+                true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y, r);
+
+        // Drag to the left (no change).
+        mPositioner.resizeDrag(0.0f, r.top);
+        assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the right (no change).
+        mPositioner.resizeDrag(2000.0f, r.top);
+        assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the top.
+        mPositioner.resizeDrag(300.0f, 0.0f);
+        int h = r.bottom - MOUSE_DELTA_Y;
+        int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT));
+        assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the bottom.
+        mPositioner.resizeDrag(r.left, 1000.0f);
+        h = mMinVisibleHeight;
+        assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    /**
+     * This tests that a constrained portrait window will keep the aspect and do the
+     * right things upon resizing when dragged from the top left corner.
+     */
+    @Test
+    public void testPortraitPreservedWindowResizingDragTopLeft() throws Exception {
+        final Rect r = new Rect(330, 100, 630, 600);
+
+        mPositioner.startDrag(true /*resizing*/,
+                true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y, r);
+        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
+
+        // Drag to a good landscape size.
+        mPositioner.resizeDrag(0.0f, 0.0f);
+        int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT);
+        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a good portrait size.
+        mPositioner.resizeDrag(500.0f, 0.0f);
+        assertBoundsEquals(new Rect(500 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to a too small size for the height and the the width shrinking.
+        mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f);
+        final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT));
+        final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT));
+        assertBoundsEquals(
+                new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    /**
+     * This tests that a constrained portrait window will keep the aspect and do the
+     * right things upon resizing when dragged from the left corner.
+     */
+    @Test
+    public void testPortraitPreservedWindowResizingDragLeft() throws Exception {
+        final Rect r = new Rect(330, 100, 630, 600);
+        final int midY = (r.top + r.bottom) / 2;
+
+        mPositioner.startDrag(true /*resizing*/,
+                true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY, r);
+
+        // Drag to the left.
+        mPositioner.resizeDrag(0.0f, midY);
+        int w = r.right - MOUSE_DELTA_X;
+        int h = Math.round(w * MIN_ASPECT);
+        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the right.
+        mPositioner.resizeDrag(450.0f, midY);
+        assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag all the way to the right.
+        mPositioner.resizeDrag(2000.0f, midY);
+        w = mMinVisibleWidth;
+        h = Math.max(Math.round((float)w * MIN_ASPECT), r.height());
+        assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the top.
+        mPositioner.resizeDrag(r.left, 0.0f);
+        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the bottom.
+        mPositioner.resizeDrag(r.left, 1000.0f);
+        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    /**
+     * This tests that a constrained portrait window will keep the aspect and do the
+     * right things upon resizing when dragged from the top corner.
+     */
+    @Test
+    public void testPortraitPreservedWindowResizingDragTop() throws Exception {
+        final Rect r = new Rect(330, 100, 630, 600);
+        final int midX = (r.left + r.right) / 2;
+
+        mPositioner.startDrag(true /*resizing*/,
+                true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y, r);
+
+        // Drag to the left (no change).
+        mPositioner.resizeDrag(0.0f, r.top);
+        assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the right (no change).
+        mPositioner.resizeDrag(2000.0f, r.top);
+        assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the top.
+        mPositioner.resizeDrag(300.0f, 0.0f);
+        int h = r.bottom - MOUSE_DELTA_Y;
+        int w = Math.min(r.width(), Math.round(h / MIN_ASPECT));
+        assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
+                mPositioner.getWindowDragBounds());
+
+        // Drag to the bottom.
+        mPositioner.resizeDrag(r.left, 1000.0f);
+        h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT));
+        w = Math.round(h / MIN_ASPECT);
+        assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom),
+                mPositioner.getWindowDragBounds());
+    }
+
+    private void assertBoundsEquals(Rect expected, Rect actual) {
+        if (DEBUGGING) {
+            if (!expected.equals(actual)) {
+                Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
+                        + ") " + Log.getStackTraceString(new Throwable()));
+            }
+        }
+        assertEquals(expected.left, actual.left);
+        assertEquals(expected.right, actual.right);
+        assertEquals(expected.top, actual.top);
+        assertEquals(expected.bottom, actual.bottom);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 36bd13a..a820eb1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -45,7 +45,6 @@
  * runtest frameworks-services -c com.android.server.wm.UnknownVisibilityControllerTest
  */
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class UnknownAppVisibilityControllerTest {
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 3f47d5c..df35b7ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -16,23 +16,18 @@
 
 package com.android.server.wm;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import android.content.Context;
-import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.IWindow;
-import android.view.WindowManager;
 
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -41,30 +36,19 @@
 /**
  * Tests for the {@link WindowState} class.
  *
- * runtest frameworks-services -c com.android.server.wm.WindowStateTests
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.WindowStateTests
  */
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class WindowStateTests extends WindowTestsBase {
 
-    private WindowToken mWindowToken;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        mWindowToken = new WindowToken(sWm, new Binder(), 0, false,
-                sWm.getDefaultDisplayContentLocked());
-    }
-
     @Test
     public void testIsParentWindowHidden() throws Exception {
-        final WindowState parentWindow =
-                createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow");
-        final WindowState child1 =
-                createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child1");
-        final WindowState child2 =
-                createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child2");
+        final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
+        final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
 
         assertFalse(parentWindow.mHidden);
         assertFalse(parentWindow.isParentWindowHidden());
@@ -79,14 +63,10 @@
 
     @Test
     public void testIsChildWindow() throws Exception {
-        final WindowState parentWindow =
-                createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow");
-        final WindowState child1 =
-                createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child1");
-        final WindowState child2 =
-                createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child2");
-        final WindowState randomWindow =
-                createWindow(null, TYPE_APPLICATION, mWindowToken, "randomWindow");
+        final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
+        final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
+        final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow");
 
         assertFalse(parentWindow.isChildWindow());
         assertTrue(child1.isChildWindow());
@@ -96,13 +76,12 @@
 
     @Test
     public void testHasChild() throws Exception {
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, mWindowToken, "win1");
-        final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, mWindowToken, "win11");
-        final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, mWindowToken, "win12");
-        final WindowState win2 = createWindow(null, TYPE_APPLICATION, mWindowToken, "win2");
-        final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, mWindowToken, "win21");
-        final WindowState randomWindow =
-                createWindow(null, TYPE_APPLICATION, mWindowToken, "randomWindow");
+        final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1");
+        final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11");
+        final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12");
+        final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2");
+        final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21");
+        final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow");
 
         assertTrue(win1.hasChild(win11));
         assertTrue(win1.hasChild(win12));
@@ -118,12 +97,9 @@
 
     @Test
     public void testGetParentWindow() throws Exception {
-        final WindowState parentWindow =
-                createWindow(null, TYPE_APPLICATION, mWindowToken, "parentWindow");
-        final WindowState child1 =
-                createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child1");
-        final WindowState child2 =
-                createWindow(parentWindow, FIRST_SUB_WINDOW, mWindowToken, "child2");
+        final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
+        final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
 
         assertNull(parentWindow.getParentWindow());
         assertEquals(parentWindow, child1.getParentWindow());
@@ -132,22 +108,63 @@
 
     @Test
     public void testGetTopParentWindow() throws Exception {
-        final WindowState root = createWindow(null, TYPE_APPLICATION, mWindowToken, "root");
-        final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, mWindowToken, "child1");
-        final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, mWindowToken, "child2");
+        final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+        final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
+        final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
 
         assertEquals(root, root.getTopParentWindow());
         assertEquals(root, child1.getTopParentWindow());
         assertEquals(child1, child2.getParentWindow());
         assertEquals(root, child2.getTopParentWindow());
+
+        // Test case were child is detached from parent.
+        root.removeChild(child1);
+        assertEquals(child1, child1.getTopParentWindow());
+        assertEquals(child1, child2.getParentWindow());
     }
 
     @Test
     public void testIsOnScreen_hiddenByPolicy() {
-        final WindowState window = createWindow(null, TYPE_APPLICATION, mWindowToken, "window");
+        final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
         window.setHasSurface(true);
         assertTrue(window.isOnScreen());
         window.hideLw(false /* doAnimation */);
         assertFalse(window.isOnScreen());
     }
+
+    @Test
+    public void testCanBeImeTarget() throws Exception {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
+
+        // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being
+        // an IME target.
+        appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+        imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+        // Make windows visible
+        appWindow.setHasSurface(true);
+        imeWindow.setHasSurface(true);
+
+        // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets
+        assertFalse(appWindow.canBeImeTarget());
+        assertFalse(imeWindow.canBeImeTarget());
+
+        // Add IME target flags
+        appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+        imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+
+        // Visible app window with flags can be IME target while an IME window can never be an IME
+        // target regardless of its visibility or flags.
+        assertTrue(appWindow.canBeImeTarget());
+        assertFalse(imeWindow.canBeImeTarget());
+
+        // Make windows invisible
+        appWindow.hideLw(false /* doAnimation */);
+        imeWindow.hideLw(false /* doAnimation */);
+
+        // Invisible window can't be IME targets even if they have the right flags.
+        assertFalse(appWindow.canBeImeTarget());
+        assertFalse(imeWindow.canBeImeTarget());
+    }
 }
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 3a69537..ab6968b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -113,6 +113,12 @@
         return token;
     }
 
+    WindowState createWindow(WindowState parent, int type, String name) {
+        return (parent == null)
+                ? createWindow(parent, type, sDisplayContent, name)
+                : createWindow(parent, type, parent.mToken, name);
+    }
+
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
         final WindowToken token = createWindowToken(dc, type);
         return createWindow(parent, type, token, name);
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index a012082..177759e 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -28,6 +28,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -453,6 +454,10 @@
      * @param conferenceableConnections The set of connections this connection can conference with.
      */
     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
+        if (Objects.equals(mConferenceableConnections, conferenceableConnections)) {
+            return;
+        }
+
         clearConferenceableList();
         for (Connection c : conferenceableConnections) {
             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index a5e8fe1..cd65232 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -118,7 +118,7 @@
         String defaultPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
                 Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
 
-        final List<String> packageNames = getInstalledDialerApplications(context);
+        final List<String> packageNames = getInstalledDialerApplications(context, user);
 
         // Verify that the default dialer has not been disabled or uninstalled.
         if (packageNames.contains(defaultPackageName)) {
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 6860269..e6b567e 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -75,6 +75,14 @@
      */
     public static final int CALL_PULLED = 12;
 
+    /**
+     * Reason code (returned via {@link #getReason()}) which indicates that a call could not be
+     * completed because the cellular radio is off or out of service, the device is connected to
+     * a wifi network, but the user has not enabled wifi calling.
+     * @hide
+     */
+    public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
+
     private int mDisconnectCode;
     private CharSequence mDisconnectLabel;
     private CharSequence mDisconnectDescription;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a0f5217..e6354b7 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -658,6 +658,24 @@
     public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
 
     /**
+     * Determines whether a maximum size limit for IMS conference calls is enforced on the device.
+     * When {@code true}, IMS conference calls will be limited to at most
+     * {@link #KEY_IMS_CONFERENCE_SIZE_LIMIT_INT} participants.  When {@code false}, no attempt is made
+     * to limit the number of participants in a conference (the carrier will raise an error when an
+     * attempt is made to merge too many participants into a conference).
+     */
+    public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL =
+            "is_ims_conference_size_enforced_bool";
+
+    /**
+     * Determines the maximum number of participants the carrier supports for a conference call.
+     * This number is exclusive of the current device.  A conference between 3 devices, for example,
+     * would have a size limit of 2 participants.
+     * Enforced when {@link #KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL} is {@code true}.
+     */
+    public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
+
+    /**
      * Determines whether High Definition audio property is displayed in the dialer UI.
      * If {@code false}, remove the HD audio property from the connection so that HD audio related
      * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -1112,6 +1130,14 @@
      */
     public static final String KEY_CARRIER_WIFI_STRING_ARRAY = "carrier_wifi_string_array";
 
+    /**
+     * Time delay (in ms) after which we show the notification to switch the preferred
+     * network.
+     * @hide
+     */
+    public static final String KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT =
+            "network_notification_delay_int";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1232,6 +1258,8 @@
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
+        sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
         sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
         sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
         sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
@@ -1311,6 +1339,7 @@
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
         sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, false);
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
+        sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
     }
 
     /**
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index e443911..d5f3ce8 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -16,6 +16,7 @@
 
 package android.test.mock;
 
+import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
@@ -97,11 +98,11 @@
         }
 
         @Override
-        public Cursor query(String callingPackage, Uri url, String[] projection, String selection,
-                String[] selectionArgs,
-                String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException {
-            return MockContentProvider.this.query(url, projection, selection,
-                    selectionArgs, sortOrder);
+        public Cursor query(String callingPackage, Uri url, @Nullable String[] projection,
+                @Nullable Bundle queryArgs,
+                @Nullable ICancellationSignal cancellationSignal)
+                throws RemoteException {
+            return MockContentProvider.this.query(url, projection, queryArgs, null);
         }
 
         @Override
@@ -248,10 +249,12 @@
         throw new UnsupportedOperationException("unimplemented mock method call");
     }
 
+    @Override
     public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
         throw new UnsupportedOperationException("unimplemented mock method call");
     }
 
+    @Override
     public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) {
         throw new UnsupportedOperationException("unimplemented mock method call");
     }
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 09d45d1..112d7ee 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -16,6 +16,7 @@
 
 package android.test.mock;
 
+import android.annotation.Nullable;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentValues;
@@ -41,45 +42,52 @@
  * @hide - @hide because this exposes bulkQuery() and call(), which must also be hidden.
  */
 public class MockIContentProvider implements IContentProvider {
+    @Override
     public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     @SuppressWarnings("unused")
     public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
             throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public String getType(Uri url) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     @SuppressWarnings("unused")
     public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
             throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public ParcelFileDescriptor openFile(
             String callingPackage, Uri url, String mode, ICancellationSignal signal,
             IBinder callerToken) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public AssetFileDescriptor openAssetFile(
             String callingPackage, Uri uri, String mode, ICancellationSignal signal) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public ContentProviderResult[] applyBatch(String callingPackage,
             ArrayList<ContentProviderOperation> operations) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
-    public Cursor query(String callingPackage, Uri url, String[] projection, String selection,
-            String[] selectionArgs,
-            String sortOrder, ICancellationSignal cancellationSignal) {
+    @Override
+    public Cursor query(String callingPackage, Uri url, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
@@ -88,24 +96,29 @@
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public int update(String callingPackage, Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public Bundle call(String callingPackage, String method, String request, Bundle args)
             throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public IBinder asBinder() {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    @Override
     public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType,
             Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
         throw new UnsupportedOperationException("unimplemented mock method");
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 3785cdc..b4f3d69 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -989,5 +989,15 @@
             </intent-filter>
         </activity>
 
+        <activity
+                android:name="PixelCopyWindow"
+                android:label="Readback/Window"
+                android:screenOrientation="fullSensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java
new file mode 100644
index 0000000..a039fba
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java
@@ -0,0 +1,98 @@
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Paint.Style;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.PixelCopy;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class PixelCopyWindow extends Activity {
+
+    private Handler mHandler;
+    private ImageView mImage;
+    private TextView mText;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mHandler = new Handler();
+
+        LinearLayout layout = new LinearLayout(this);
+        TextView text = new TextView(this);
+        text.setText("Hello, World!");
+        Button btn = new Button(this);
+        btn.setText("Screenshot!");
+        btn.setOnClickListener((v) -> takeScreenshot());
+        mImage = new ImageView(this);
+        mText = new TextView(this);
+
+        layout.setOrientation(LinearLayout.VERTICAL);
+        layout.addView(text);
+        layout.addView(btn);
+        layout.addView(mImage);
+        layout.addView(mText);
+        final float density = getResources().getDisplayMetrics().density;
+        layout.setBackground(new Drawable() {
+            Paint mPaint = new Paint();
+
+            @Override
+            public void draw(Canvas canvas) {
+                mPaint.setStyle(Style.STROKE);
+                mPaint.setStrokeWidth(4 * density);
+                mPaint.setColor(Color.BLUE);
+                final Rect bounds = getBounds();
+                canvas.drawRect(bounds, mPaint);
+                mPaint.setColor(Color.RED);
+                canvas.drawLine(bounds.centerX(), 0, bounds.centerX(), bounds.height(), mPaint);
+                mPaint.setColor(Color.GREEN);
+                canvas.drawLine(0, bounds.centerY(), bounds.width(), bounds.centerY(), mPaint);
+            }
+
+            @Override
+            public void setAlpha(int alpha) {
+            }
+
+            @Override
+            public void setColorFilter(ColorFilter colorFilter) {
+            }
+
+            @Override
+            public int getOpacity() {
+                return PixelFormat.TRANSLUCENT;
+            }
+        });
+        setContentView(layout);
+    }
+
+    private void takeScreenshot() {
+        View decor = getWindow().getDecorView();
+        Rect srcRect = new Rect();
+        decor.getGlobalVisibleRect(srcRect);
+        final Bitmap bitmap = Bitmap.createBitmap(
+                (int) (srcRect.width() * .25), (int) (srcRect.height() * .25), Config.ARGB_8888);
+        PixelCopy.request(getWindow(), srcRect, bitmap, (result) -> {
+            if (result != PixelCopy.SUCCESS) {
+                mText.setText("Copy failed, result: " + result);
+                mImage.setImageBitmap(null);
+            } else {
+                mText.setText("");
+                mImage.setImageBitmap(bitmap);
+            }
+        }, mHandler);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
index 1e7dfbe..ebb2af4 100644
--- a/tools/layoutlib/bridge/src/android/view/SurfaceView.java
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.util.AttributeSet;
 
 /**
@@ -49,6 +50,19 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+    public boolean gatherTransparentRegion(Region region) {
+      return false;
+    }
+
+    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+    }
+
+    public void setZOrderOnTop(boolean onTop) {
+    }
+
+    public void setSecure(boolean isSecure) {
+    }
+
     public SurfaceHolder getHolder() {
         return mSurfaceHolder;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
index ffce1a0..da1ab27 100644
--- a/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.graphics.Path;
 import android.util.MathUtils;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
@@ -31,6 +32,7 @@
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.view.animation.OvershootInterpolator;
+import android.view.animation.PathInterpolator;
 
 /**
  * Delegate used to provide new implementation of a select few methods of {@link
@@ -93,6 +95,16 @@
         return sManager.addNewDelegate(new OvershootInterpolator(tension));
     }
 
+    @LayoutlibDelegate
+    /*package*/ static long createPathInterpolator(float[] x, float[] y) {
+        Path path = new Path();
+        path.moveTo(x[0], y[0]);
+        for (int i = 1; i < x.length; i++) {
+            path.lineTo(x[i], y[i]);
+        }
+        return sManager.addNewDelegate(new PathInterpolator(path));
+    }
+
     private static class LutInterpolator extends BaseInterpolator {
         private final float[] mValues;
         private final int mSize;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 3471165..c827f17 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -97,8 +97,8 @@
     }
 
     @Override
-    public Cursor query(String callingPackage, Uri arg0, String[] arg1, String arg2, String[] arg3,
-            String arg4, ICancellationSignal arg5) throws RemoteException {
+    public Cursor query(String callingPackage, Uri arg0, String[] arg1,
+            Bundle arg3, ICancellationSignal arg4) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index 97698df..726ff22 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -166,13 +166,13 @@
         FrameLayout contentRoot = new FrameLayout(getContext());
         LayoutParams params = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
         int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
-        if (mBuilder.hasNavBar() && mBuilder.solidBars()) {
+        if (mBuilder.hasSolidNavBar()) {
             params.addRule(rule, getId(ID_NAV_BAR));
         }
         int below = -1;
         if (mBuilder.mActionBarSize <= 0 && mBuilder.mTitleBarSize > 0) {
             below = getId(ID_TITLE_BAR);
-        } else if (mBuilder.hasStatusBar() && mBuilder.solidBars()) {
+        } else if (mBuilder.hasSolidStatusBar()) {
             below = getId(ID_STATUS_BAR);
         }
         if (below != -1) {
@@ -241,10 +241,10 @@
         }
         LayoutParams layoutParams = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
         int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
-        if (mBuilder.hasNavBar() && mBuilder.solidBars()) {
+        if (mBuilder.hasSolidNavBar()) {
             layoutParams.addRule(rule, getId(ID_NAV_BAR));
         }
-        if (mBuilder.hasStatusBar() && mBuilder.solidBars()) {
+        if (mBuilder.hasSolidStatusBar()) {
             layoutParams.addRule(BELOW, getId(ID_STATUS_BAR));
         }
         actionBar.getRootView().setLayoutParams(layoutParams);
@@ -257,10 +257,10 @@
             int simulatedPlatformVersion) {
         TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
         LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
-        if (mBuilder.hasStatusBar() && mBuilder.solidBars()) {
+        if (mBuilder.hasSolidStatusBar()) {
             params.addRule(BELOW, getId(ID_STATUS_BAR));
         }
-        if (mBuilder.isNavBarVertical() && mBuilder.solidBars()) {
+        if (mBuilder.isNavBarVertical() && mBuilder.hasSolidNavBar()) {
             params.addRule(START_OF, getId(ID_NAV_BAR));
         }
         titleBar.setLayoutParams(params);
@@ -419,11 +419,17 @@
         }
 
         /**
-         * Return true if the status bar or nav bar are present, they are not translucent (i.e
-         * content doesn't overlap with them).
+         * Return true if the nav bar is present and not translucent
          */
-        private boolean solidBars() {
-            return !(hasNavBar() && mTranslucentNav) && !(hasStatusBar() && mTranslucentStatus);
+        private boolean hasSolidNavBar() {
+            return hasNavBar() && !mTranslucentNav;
+        }
+
+        /**
+         * Return true if the status bar is present and not translucent
+         */
+        private boolean hasSolidStatusBar() {
+            return hasStatusBar() && !mTranslucentStatus;
         }
 
         private boolean hasNavBar() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java
index 7e361a1..96cba78 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/libcore/io/BridgeBufferIterator.java
@@ -40,6 +40,11 @@
     }
 
     @Override
+    public int pos() {
+        return mByteBuffer.position();
+    }
+
+    @Override
     public void skip(int byteCount) {
         int newPosition = mByteBuffer.position() + byteCount;
         assert newPosition <= mSize;
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
new file mode 100644
index 0000000..868cd51
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
new file mode 100644
index 0000000..601f711
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
new file mode 100644
index 0000000..0b8f1a9
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/four_corners.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/four_corners.xml
new file mode 100644
index 0000000..c42284a
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/four_corners.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:textSize="40sp"
+        android:text="Bottom Text" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        android:textSize="40sp"
+        android:text="Top text" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:textSize="40sp"
+        android:text="Start text" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:textSize="40sp"
+        android:text="End text" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 24cbbca..c813a12 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -39,6 +39,7 @@
 import com.android.resources.Density;
 import com.android.resources.Navigation;
 import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
 import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.utils.ILogger;
 
@@ -341,6 +342,31 @@
         renderAndVerify("activity.xml", "activity.png");
     }
 
+    @Test
+    public void testTranslucentBars() throws ClassNotFoundException {
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        LayoutPullParser parser = createLayoutPullParser("four_corners.xml");
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.Light.NoActionBar.TranslucentDecor", false,
+                RenderingMode.NORMAL, 22);
+        renderAndVerify(params, "four_corners_translucent.png");
+
+        parser = createLayoutPullParser("four_corners.xml");
+        params = getSessionParams(parser, ConfigGenerator.NEXUS_5_LAND,
+                layoutLibCallback, "Theme.Material.Light.NoActionBar.TranslucentDecor", false,
+                RenderingMode.NORMAL, 22);
+        renderAndVerify(params, "four_corners_translucent_land.png");
+
+        parser = createLayoutPullParser("four_corners.xml");
+        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
+                RenderingMode.NORMAL, 22);
+        renderAndVerify(params, "four_corners.png");
+    }
+
     private static void gc() {
         // See RuntimeUtil#gc in jlibs (http://jlibs.in/)
         Object obj = new Object();
diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk
index 769db6b..d3ee1d3 100644
--- a/tools/preload2/Android.mk
+++ b/tools/preload2/Android.mk
@@ -17,6 +17,8 @@
 LOCAL_MODULE:= preload2
 
 include $(BUILD_HOST_JAVA_LIBRARY)
+# Copy to build artifacts
+$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE):$(LOCAL_MODULE).jar)
 
 # Copy the preload-tool shell script to the host's bin directory.
 include $(CLEAR_VARS)
diff --git a/tools/streaming_proto/main.cpp b/tools/streaming_proto/main.cpp
index 5435728..5b4ba04 100644
--- a/tools/streaming_proto/main.cpp
+++ b/tools/streaming_proto/main.cpp
@@ -94,7 +94,7 @@
  * Figure out the name of the file we are generating.
  */
 static string
-make_file_name(const FileDescriptorProto& file_descriptor)
+make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name)
 {
     string const package = make_java_package(file_descriptor);
     string result;
@@ -103,7 +103,7 @@
         result += '/';
     }
 
-    result += make_outer_class_name(file_descriptor);
+    result += class_name;
     result += ".java";
 
     return result;
@@ -320,10 +320,16 @@
 
 /**
  * Write the contents of a file.
+ *
+ * If there are enums and generate_outer is false, invalid java code will be generated.
  */
 static void
-write_file(stringstream& text, const FileDescriptorProto& file_descriptor)
+write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor,
+        const string& filename, bool generate_outer,
+        const vector<EnumDescriptorProto>& enums, const vector<DescriptorProto>& messages)
 {
+    stringstream text;
+
     string const package_name = make_java_package(file_descriptor);
     string const outer_class_name = make_outer_class_name(file_descriptor);
 
@@ -338,27 +344,92 @@
     }
 
     // This bit of policy is android api rules specific: Raw proto classes
-    // must never be in the API, but they should all be available for testing.
+    // must never be in the API
     text << "/** @hide */" << endl;
-    text << "@android.annotation.TestApi" << endl;
+//    text << "@android.annotation.TestApi" << endl;
 
-    text << "public final class " << outer_class_name << " {" << endl;
-    text << endl;
+    if (generate_outer) {
+        text << "public final class " << outer_class_name << " {" << endl;
+        text << endl;
+    }
 
-    int N;
-    const string indented = indent_more("");
+    size_t N;
+    const string indented = generate_outer ? indent_more("") : string();
     
+    N = enums.size();
+    for (size_t i=0; i<N; i++) {
+        write_enum(text, enums[i], indented);
+    }
+
+    N = messages.size();
+    for (size_t i=0; i<N; i++) {
+        write_message(text, messages[i], indented);
+    }
+
+    if (generate_outer) {
+        text << "}" << endl;
+    }
+
+    CodeGeneratorResponse::File* file_response = response->add_file();
+    file_response->set_name(filename);
+    file_response->set_content(text.str());
+}
+
+/**
+ * Write one file per class.  Put all of the enums into the "outer" class.
+ */
+static void
+write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+{
+    // If there is anything to put in the outer class file, create one
+    if (file_descriptor.enum_type_size() > 0) {
+        vector<EnumDescriptorProto> enums;
+        int N = file_descriptor.enum_type_size();
+        for (int i=0; i<N; i++) {
+            enums.push_back(file_descriptor.enum_type(i));
+        }
+
+        vector<DescriptorProto> messages;
+
+        write_file(response, file_descriptor,
+                make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
+                true, enums, messages);
+    }
+
+    // For each of the message types, make a file
+    int N = file_descriptor.message_type_size();
+    for (int i=0; i<N; i++) {
+        vector<EnumDescriptorProto> enums;
+
+        vector<DescriptorProto> messages;
+        messages.push_back(file_descriptor.message_type(i));
+
+        write_file(response, file_descriptor,
+                make_file_name(file_descriptor, file_descriptor.message_type(i).name()),
+                false, enums, messages);
+    }
+}
+
+static void
+write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+{
+    int N;
+
+    vector<EnumDescriptorProto> enums;
     N = file_descriptor.enum_type_size();
     for (int i=0; i<N; i++) {
-        write_enum(text, file_descriptor.enum_type(i), indented);
+        enums.push_back(file_descriptor.enum_type(i));
     }
 
+    vector<DescriptorProto> messages;
     N = file_descriptor.message_type_size();
     for (int i=0; i<N; i++) {
-        write_message(text, file_descriptor.message_type(i), indented);
+        messages.push_back(file_descriptor.message_type(i));
     }
 
-    text << "}" << endl;
+    write_file(response, file_descriptor,
+            make_file_name(file_descriptor, make_outer_class_name(file_descriptor)),
+            true, enums, messages);
 }
 
 /**
@@ -383,16 +454,11 @@
     for (int i=0; i<N; i++) {
         const FileDescriptorProto& file_descriptor = request.proto_file(i);
         if (should_generate_for_file(request, file_descriptor.name())) {
-            // Generate the text
-            stringstream text;
-            write_file(text, file_descriptor);
-
-            // Put the text in the response
-            CodeGeneratorResponse::File* file_response = response.add_file();
-            file_response->set_name(make_file_name(file_descriptor));
-            file_response->set_content(text.str());
-
-            cerr << "writing file: " << file_response->name() << endl;
+            if (file_descriptor.options().java_multiple_files()) {
+                write_multiple_files(&response, file_descriptor);
+            } else {
+                write_single_file(&response, file_descriptor);
+            }
         }
     }
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 69e9fcd..43e6246 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -746,50 +746,6 @@
     @SystemApi
     public int numAssociation;
 
-    /**
-     * @hide
-     * Number of time user disabled WiFi while associated to this configuration with Low RSSI.
-     */
-    public int numUserTriggeredWifiDisableLowRSSI;
-
-    /**
-     * @hide
-     * Number of time user disabled WiFi while associated to this configuration with Bad RSSI.
-     */
-    public int numUserTriggeredWifiDisableBadRSSI;
-
-    /**
-     * @hide
-     * Number of time user disabled WiFi while associated to this configuration
-     * and RSSI was not HIGH.
-     */
-    public int numUserTriggeredWifiDisableNotHighRSSI;
-
-    /**
-     * @hide
-     * Number of ticks associated to this configuration with Low RSSI.
-     */
-    public int numTicksAtLowRSSI;
-
-    /**
-     * @hide
-     * Number of ticks associated to this configuration with Bad RSSI.
-     */
-    public int numTicksAtBadRSSI;
-
-    /**
-     * @hide
-     * Number of ticks associated to this configuration
-     * and RSSI was not HIGH.
-     */
-    public int numTicksAtNotHighRSSI;
-    /**
-     * @hide
-     * Number of time user (WifiManager) triggered association to this configuration.
-     * TODO: count this only for Wifi Settings uuid, so as to not count 3rd party apps
-     */
-    public int numUserTriggeredJoinAttempts;
-
     /** @hide
      * Boost given to RSSI on a home network for the purpose of calculating the score
      * This adds stickiness to home networks, as defined by:
@@ -1630,16 +1586,6 @@
                 sbuf.append('\n');
             }
         }
-        sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
-        sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
-        sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
-        sbuf.append('\n');
-        sbuf.append("ticksLow: ").append(this.numTicksAtLowRSSI);
-        sbuf.append(" ticksBad: ").append(this.numTicksAtBadRSSI);
-        sbuf.append(" ticksNotHigh: ").append(this.numTicksAtNotHighRSSI);
-        sbuf.append('\n');
-        sbuf.append("triggeredJoin: ").append(this.numUserTriggeredJoinAttempts);
-        sbuf.append('\n');
 
         return sbuf.toString();
     }
@@ -1946,13 +1892,6 @@
             numScorerOverride = source.numScorerOverride;
             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
             numAssociation = source.numAssociation;
-            numUserTriggeredWifiDisableLowRSSI = source.numUserTriggeredWifiDisableLowRSSI;
-            numUserTriggeredWifiDisableBadRSSI = source.numUserTriggeredWifiDisableBadRSSI;
-            numUserTriggeredWifiDisableNotHighRSSI = source.numUserTriggeredWifiDisableNotHighRSSI;
-            numTicksAtLowRSSI = source.numTicksAtLowRSSI;
-            numTicksAtBadRSSI = source.numTicksAtBadRSSI;
-            numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
-            numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
             userApproved = source.userApproved;
             numNoInternetAccessReports = source.numNoInternetAccessReports;
             noInternetAccessExpected = source.noInternetAccessExpected;
@@ -2018,13 +1957,6 @@
         dest.writeInt(numScorerOverride);
         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
         dest.writeInt(numAssociation);
-        dest.writeInt(numUserTriggeredWifiDisableLowRSSI);
-        dest.writeInt(numUserTriggeredWifiDisableBadRSSI);
-        dest.writeInt(numUserTriggeredWifiDisableNotHighRSSI);
-        dest.writeInt(numTicksAtLowRSSI);
-        dest.writeInt(numTicksAtBadRSSI);
-        dest.writeInt(numTicksAtNotHighRSSI);
-        dest.writeInt(numUserTriggeredJoinAttempts);
         dest.writeInt(userApproved);
         dest.writeInt(numNoInternetAccessReports);
         dest.writeInt(noInternetAccessExpected ? 1 : 0);
@@ -2090,13 +2022,6 @@
                 config.numScorerOverride = in.readInt();
                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
                 config.numAssociation = in.readInt();
-                config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
-                config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
-                config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
-                config.numTicksAtLowRSSI = in.readInt();
-                config.numTicksAtBadRSSI = in.readInt();
-                config.numTicksAtNotHighRSSI = in.readInt();
-                config.numUserTriggeredJoinAttempts = in.readInt();
                 config.userApproved = in.readInt();
                 config.numNoInternetAccessReports = in.readInt();
                 config.noInternetAccessExpected = in.readInt() != 0;
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
new file mode 100644
index 0000000..5f949747
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiConfiguration}.
+ */
+public class WifiConfigurationTest {
+
+    @Before
+    public void setUp() {
+    }
+
+    /**
+     * Check that parcel marshalling/unmarshalling works
+     *
+     * Create and populate a WifiConfiguration.
+     * Marshall and unmashall it, and expect to recover a copy of the original.
+     * Marshall the resulting object, and expect the bytes to match the
+     * first marshall result.
+     */
+    @Test
+    public void testWifiConfigurationParcel() {
+        String cookie = "C O.o |<IE";
+        WifiConfiguration config = new WifiConfiguration();
+        config.setPasspointManagementObjectTree(cookie);
+        Parcel parcelW = Parcel.obtain();
+        config.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiConfiguration reconfig = WifiConfiguration.CREATOR.createFromParcel(parcelR);
+
+        // lacking a useful config.equals, check one field near the end.
+        assertEquals(cookie, reconfig.getMoTree());
+
+        Parcel parcelWW = Parcel.obtain();
+        reconfig.writeToParcel(parcelWW, 0);
+        byte[] rebytes = parcelWW.marshall();
+        parcelWW.recycle();
+
+        assertArrayEquals(bytes, rebytes);
+    }
+}