Merge "Move device serial behing a permission"
diff --git a/Android.mk b/Android.mk
index 8977433..5e3b50a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1240,6 +1240,31 @@
 
 include $(BUILD_DROIDDOC)
 
+# ==== generates full navtree for resolving @links in ds postprocessing ====
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
+LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
+LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
+LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
+
+LOCAL_MODULE := ds-ref-navtree
+
+LOCAL_DROIDDOC_OPTIONS:= \
+		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+		-hdf android.whichdoc online \
+		-toroot / \
+		-atLinksNavtree \
+		-navtreeonly
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+
+include $(BUILD_DROIDDOC)
+
 # ==== site updates for docs (on the androiddevdocs app engine server) =======================
 include $(CLEAR_VARS)
 
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index 13a5e83..ce0c357 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -41,7 +41,6 @@
 import android.perftests.utils.StubActivity;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.test.annotation.UiThreadTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.InstrumentationRegistry;
 
@@ -106,20 +105,21 @@
     }
 
     @Test
-    @UiThreadTest
-    public void testEditText() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final EditText editText = setupEditText();
-        final KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
-        final int steps = 100;
-        while (state.keepRunning()) {
-            for (int i = 0; i < steps; i++) {
-                int offset = (editText.getText().length() * i) / steps;
-                editText.setSelection(offset);
-                editText.bringPointIntoView(offset);
-                editText.onKeyDown(keyEvent.getKeyCode(), keyEvent);
-                editText.updateDisplayListIfDirty();
+    public void testEditText() throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            final EditText editText = setupEditText();
+            final KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
+            final int steps = 100;
+            while (state.keepRunning()) {
+                for (int i = 0; i < steps; i++) {
+                    int offset = (editText.getText().length() * i) / steps;
+                    editText.setSelection(offset);
+                    editText.bringPointIntoView(offset);
+                    editText.onKeyDown(keyEvent.getKeyCode(), keyEvent);
+                    editText.updateDisplayListIfDirty();
+                }
             }
-        }
+        });
     }
 }
diff --git a/api/current.txt b/api/current.txt
index 6a182f0..25c6ab3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4923,6 +4923,7 @@
     method public int describeContents();
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
+    method public java.lang.String getNotificationChannel();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
     method public void writeToParcel(android.os.Parcel, int);
@@ -5111,6 +5112,7 @@
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setChannel(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
@@ -5259,6 +5261,7 @@
     method public android.app.Notification.Builder extend(android.app.Notification.Builder);
     method public java.util.List<android.app.Notification.Action> getActions();
     method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
     method public int getContentAction();
     method public int getContentIcon();
     method public int getContentIconGravity();
@@ -5277,6 +5280,7 @@
     method public java.util.List<android.app.Notification> getPages();
     method public boolean getStartScrollBottom();
     method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
     method public android.app.Notification.WearableExtender setContentAction(int);
     method public android.app.Notification.WearableExtender setContentIcon(int);
     method public android.app.Notification.WearableExtender setContentIconGravity(int);
@@ -5304,17 +5308,40 @@
     field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
   }
 
+  public final class NotificationChannel implements android.os.Parcelable {
+    ctor public NotificationChannel(java.lang.String, java.lang.CharSequence);
+    ctor protected NotificationChannel(android.os.Parcel);
+    method public boolean canBypassDnd();
+    method public int describeContents();
+    method public android.net.Uri getDefaultRingtone();
+    method public java.lang.String getId();
+    method public int getImportance();
+    method public java.lang.CharSequence getName();
+    method public void setDefaultRingtone(android.net.Uri);
+    method public void setLights(boolean);
+    method public void setVibration(boolean);
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
+    field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void deleteNotificationChannel(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
+    method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
@@ -5323,6 +5350,7 @@
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
+    method public void updateNotificationChannel(android.app.NotificationChannel);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -22587,8 +22615,8 @@
     method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.media.session.MediaSession.Token getSessionToken();
-    method public boolean getShuffleMode();
     method public android.media.session.MediaController.TransportControls getTransportControls();
+    method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.media.session.MediaController.Callback);
     method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
@@ -22637,7 +22665,7 @@
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
     method public void setRepeatMode(int);
-    method public void setShuffleMode(boolean);
+    method public void setShuffleModeEnabled(boolean);
     method public void skipToNext();
     method public void skipToPrevious();
     method public void skipToQueueItem(long);
@@ -22666,7 +22694,7 @@
     method public void setRatingType(int);
     method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleMode(boolean);
+    method public void setShuffleModeEnabled(boolean);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
@@ -22690,7 +22718,7 @@
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
     method public void onSetRepeatMode(int);
-    method public void onSetShuffleMode(boolean);
+    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -22752,7 +22780,7 @@
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
     field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE = 524288L; // 0x80000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
@@ -31791,7 +31819,7 @@
     field public static final java.lang.String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
   }
 
-  public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
+  public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
     field public static final java.lang.String QUERY_PARAMETER_SIP_ADDRESS = "sip";
@@ -34795,6 +34823,8 @@
     method public void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
+    method public static final void requestRebind(android.content.ComponentName);
+    method public final void requestUnbind();
     field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
     field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
@@ -36880,6 +36910,7 @@
     field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
+    field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
     field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -36889,6 +36920,8 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
+    field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+    field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -36941,6 +36974,7 @@
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
     field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
+    field public static final java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
     field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
@@ -47308,8 +47342,8 @@
   public class OverScroller {
     ctor public OverScroller(android.content.Context);
     ctor public OverScroller(android.content.Context, android.view.animation.Interpolator);
-    ctor public OverScroller(android.content.Context, android.view.animation.Interpolator, float, float);
-    ctor public OverScroller(android.content.Context, android.view.animation.Interpolator, float, float, boolean);
+    ctor public deprecated OverScroller(android.content.Context, android.view.animation.Interpolator, float, float);
+    ctor public deprecated OverScroller(android.content.Context, android.view.animation.Interpolator, float, float, boolean);
     method public void abortAnimation();
     method public boolean computeScrollOffset();
     method public void fling(int, int, int, int, int, int, int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index d5ec878..49a976e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5068,6 +5068,7 @@
     method public int describeContents();
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
+    method public java.lang.String getNotificationChannel();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
     method public void writeToParcel(android.os.Parcel, int);
@@ -5258,6 +5259,7 @@
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setChannel(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
@@ -5406,6 +5408,7 @@
     method public android.app.Notification.Builder extend(android.app.Notification.Builder);
     method public java.util.List<android.app.Notification.Action> getActions();
     method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
     method public int getContentAction();
     method public int getContentIcon();
     method public int getContentIconGravity();
@@ -5424,6 +5427,7 @@
     method public java.util.List<android.app.Notification> getPages();
     method public boolean getStartScrollBottom();
     method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
     method public android.app.Notification.WearableExtender setContentAction(int);
     method public android.app.Notification.WearableExtender setContentIcon(int);
     method public android.app.Notification.WearableExtender setContentIconGravity(int);
@@ -5451,17 +5455,48 @@
     field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
   }
 
+  public final class NotificationChannel implements android.os.Parcelable {
+    ctor public NotificationChannel(java.lang.String, java.lang.CharSequence);
+    ctor protected NotificationChannel(android.os.Parcel);
+    method public boolean canBypassDnd();
+    method public int describeContents();
+    method public android.net.Uri getDefaultRingtone();
+    method public java.lang.String getId();
+    method public int getImportance();
+    method public int getLockscreenVisibility();
+    method public java.lang.CharSequence getName();
+    method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
+    method public void setBypassDnd(boolean);
+    method public void setDefaultRingtone(android.net.Uri);
+    method public void setImportance(int);
+    method public void setLights(boolean);
+    method public void setLockscreenVisibility(int);
+    method public void setName(java.lang.CharSequence);
+    method public void setVibration(boolean);
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public org.json.JSONObject toJson() throws org.json.JSONException;
+    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
+    field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void deleteNotificationChannel(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
+    method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
@@ -5470,6 +5505,7 @@
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
+    method public void updateNotificationChannel(android.app.NotificationChannel);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -24183,8 +24219,8 @@
     method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.media.session.MediaSession.Token getSessionToken();
-    method public boolean getShuffleMode();
     method public android.media.session.MediaController.TransportControls getTransportControls();
+    method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.media.session.MediaController.Callback);
     method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
@@ -24233,7 +24269,7 @@
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
     method public void setRepeatMode(int);
-    method public void setShuffleMode(boolean);
+    method public void setShuffleModeEnabled(boolean);
     method public void skipToNext();
     method public void skipToPrevious();
     method public void skipToQueueItem(long);
@@ -24262,7 +24298,7 @@
     method public void setRatingType(int);
     method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleMode(boolean);
+    method public void setShuffleModeEnabled(boolean);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
@@ -24286,7 +24322,7 @@
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
     method public void onSetRepeatMode(int);
-    method public void onSetShuffleMode(boolean);
+    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -24348,7 +24384,7 @@
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
     field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE = 524288L; // 0x80000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
@@ -34437,7 +34473,7 @@
     field public static final java.lang.String STATE = "state";
   }
 
-  public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
+  public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
     field public static final java.lang.String QUERY_PARAMETER_SIP_ADDRESS = "sip";
@@ -37563,6 +37599,8 @@
     method public void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
+    method public static final void requestRebind(android.content.ComponentName);
+    method public final void requestUnbind();
     field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
     field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
@@ -37648,6 +37686,7 @@
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     field public static final int REASON_APP_CANCEL = 8; // 0x8
     field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+    field public static final int REASON_CHANNEL_BANNED = 17; // 0x11
     field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
     field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
     field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
@@ -39962,6 +40001,7 @@
     field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
+    field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
     field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -39971,6 +40011,8 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
+    field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+    field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -40023,6 +40065,7 @@
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
     field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
+    field public static final java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
     field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
@@ -50836,8 +50879,8 @@
   public class OverScroller {
     ctor public OverScroller(android.content.Context);
     ctor public OverScroller(android.content.Context, android.view.animation.Interpolator);
-    ctor public OverScroller(android.content.Context, android.view.animation.Interpolator, float, float);
-    ctor public OverScroller(android.content.Context, android.view.animation.Interpolator, float, float, boolean);
+    ctor public deprecated OverScroller(android.content.Context, android.view.animation.Interpolator, float, float);
+    ctor public deprecated OverScroller(android.content.Context, android.view.animation.Interpolator, float, float, boolean);
     method public void abortAnimation();
     method public boolean computeScrollOffset();
     method public void fling(int, int, int, int, int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index d94e712..8b0b0e8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4926,6 +4926,7 @@
     method public int describeContents();
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
+    method public java.lang.String getNotificationChannel();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
     method public void writeToParcel(android.os.Parcel, int);
@@ -5114,6 +5115,7 @@
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setChannel(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
@@ -5262,6 +5264,7 @@
     method public android.app.Notification.Builder extend(android.app.Notification.Builder);
     method public java.util.List<android.app.Notification.Action> getActions();
     method public android.graphics.Bitmap getBackground();
+    method public java.lang.String getBridgeTag();
     method public int getContentAction();
     method public int getContentIcon();
     method public int getContentIconGravity();
@@ -5280,6 +5283,7 @@
     method public java.util.List<android.app.Notification> getPages();
     method public boolean getStartScrollBottom();
     method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
     method public android.app.Notification.WearableExtender setContentAction(int);
     method public android.app.Notification.WearableExtender setContentIcon(int);
     method public android.app.Notification.WearableExtender setContentIconGravity(int);
@@ -5307,17 +5311,40 @@
     field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
   }
 
+  public final class NotificationChannel implements android.os.Parcelable {
+    ctor public NotificationChannel(java.lang.String, java.lang.CharSequence);
+    ctor protected NotificationChannel(android.os.Parcel);
+    method public boolean canBypassDnd();
+    method public int describeContents();
+    method public android.net.Uri getDefaultRingtone();
+    method public java.lang.String getId();
+    method public int getImportance();
+    method public java.lang.CharSequence getName();
+    method public void setDefaultRingtone(android.net.Uri);
+    method public void setLights(boolean);
+    method public void setVibration(boolean);
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
+    field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void deleteNotificationChannel(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
+    method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
@@ -5326,6 +5353,7 @@
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
+    method public void updateNotificationChannel(android.app.NotificationChannel);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -22659,8 +22687,8 @@
     method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.media.session.MediaSession.Token getSessionToken();
-    method public boolean getShuffleMode();
     method public android.media.session.MediaController.TransportControls getTransportControls();
+    method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.media.session.MediaController.Callback);
     method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
@@ -22709,7 +22737,7 @@
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
     method public void setRepeatMode(int);
-    method public void setShuffleMode(boolean);
+    method public void setShuffleModeEnabled(boolean);
     method public void skipToNext();
     method public void skipToPrevious();
     method public void skipToQueueItem(long);
@@ -22738,7 +22766,7 @@
     method public void setRatingType(int);
     method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleMode(boolean);
+    method public void setShuffleModeEnabled(boolean);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
@@ -22762,7 +22790,7 @@
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
     method public void onSetRepeatMode(int);
-    method public void onSetShuffleMode(boolean);
+    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -22824,7 +22852,7 @@
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
     field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE = 524288L; // 0x80000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
@@ -31868,7 +31896,7 @@
     field public static final java.lang.String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
   }
 
-  public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
+  public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
     field public static final java.lang.String QUERY_PARAMETER_SIP_ADDRESS = "sip";
@@ -34875,6 +34903,8 @@
     method public void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
+    method public static final void requestRebind(android.content.ComponentName);
+    method public final void requestUnbind();
     field public static final java.lang.String EXTRA_RULE_ID = "android.service.notification.extra.RULE_ID";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
     field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
@@ -36960,6 +36990,7 @@
     field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
+    field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
     field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -36969,6 +37000,8 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
+    field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+    field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -37021,6 +37054,7 @@
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
     field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
     field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
+    field public static final java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
     field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
@@ -42099,6 +42133,7 @@
     method public final void offsetLocation(float, float);
     method public final void recycle();
     method public final void setAction(int);
+    method public final void setActionButton(int);
     method public final void setEdgeFlags(int);
     method public final void setLocation(float, float);
     method public final void setSource(int);
@@ -44329,6 +44364,7 @@
     method public void setMaxTextLength(int);
     method public void setMovementGranularities(int);
     method public void setMultiLine(boolean);
+    method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
     method public void setPackageName(java.lang.CharSequence);
     method public void setParent(android.view.View);
     method public void setParent(android.view.View, int);
@@ -44534,6 +44570,7 @@
     method public static android.view.accessibility.AccessibilityWindowInfo obtain();
     method public static android.view.accessibility.AccessibilityWindowInfo obtain(android.view.accessibility.AccessibilityWindowInfo);
     method public void recycle();
+    method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityWindowInfo> CREATOR;
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
@@ -47393,8 +47430,8 @@
   public class OverScroller {
     ctor public OverScroller(android.content.Context);
     ctor public OverScroller(android.content.Context, android.view.animation.Interpolator);
-    ctor public OverScroller(android.content.Context, android.view.animation.Interpolator, float, float);
-    ctor public OverScroller(android.content.Context, android.view.animation.Interpolator, float, float, boolean);
+    ctor public deprecated OverScroller(android.content.Context, android.view.animation.Interpolator, float, float);
+    ctor public deprecated OverScroller(android.content.Context, android.view.animation.Interpolator, float, float, boolean);
     method public void abortAnimation();
     method public boolean computeScrollOffset();
     method public void fling(int, int, int, int, int, int, int, int);
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 80af5ea..18ad43e 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -185,12 +185,7 @@
 int main(int argc, char* const argv[])
 {
     if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
-        // EINVAL. Don't die on such kernels.
-        if (errno != EINVAL) {
-            LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
-            return 12;
-        }
+        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
     }
 
     AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
@@ -309,6 +304,5 @@
         fprintf(stderr, "Error: no class name or --zygote supplied.\n");
         app_usage();
         LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
-        return 10;
     }
 }
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
index 4983b9a..c546072 100644
--- a/cmds/bootanimation/audioplay.cpp
+++ b/cmds/bootanimation/audioplay.cpp
@@ -141,13 +141,27 @@
     // configure audio source
     SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
 
+    // Determine channelMask from num_channels
+    SLuint32 channelMask;
+    switch (chunkFormat->num_channels) {
+        case 1:
+            channelMask = SL_SPEAKER_FRONT_CENTER;
+            break;
+        case 2:
+            channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+            break;
+        default:
+            // Default of 0 will derive mask from num_channels and log a warning.
+            channelMask = 0;
+    }
+
     SLDataFormat_PCM format_pcm = {
         SL_DATAFORMAT_PCM,
         chunkFormat->num_channels,
         chunkFormat->sample_rate * 1000,  // convert to milliHz
         chunkFormat->bits_per_sample,
         16,
-        SL_SPEAKER_FRONT_CENTER,
+        channelMask,
         SL_BYTEORDER_LITTLEENDIAN
     };
     SLDataSource audioSrc = {&loc_bufq, &format_pcm};
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 2e9d0d6..0e0ecd0 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -230,8 +230,8 @@
         }
 
         @Override
-        public void onShuffleModeChanged(boolean shuffleMode) throws RemoteException {
-            System.out.println("onShuffleModeChanged " + shuffleMode);
+        public void onShuffleModeChanged(boolean enabled) throws RemoteException {
+            System.out.println("onShuffleModeChanged " + enabled);
         }
 
         void printUsageMessage() {
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index 383cd01..84fb626 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -21,16 +21,22 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.AndroidException;
 import android.util.DisplayMetrics;
+import android.system.Os;
 import android.view.Display;
 import android.view.IWindowManager;
 import com.android.internal.os.BaseCommand;
 
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.DataInputStream;
 import java.io.PrintStream;
+import java.lang.Runtime;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -69,7 +75,9 @@
                 "wm screen-capture: enable/disable screen capture.\n" +
                 "\n" +
                 "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
-                "necessary.\n"
+                "necessary.\n" +
+                "\n" +
+                "wm surface-trace: log surface commands to stdout in a binary format.\n"
                 );
     }
 
@@ -96,12 +104,29 @@
             runSetScreenCapture();
         } else if (op.equals("dismiss-keyguard")) {
             runDismissKeyguard();
+        } else if (op.equals("surface-trace")) {
+            runSurfaceTrace();
         } else {
             showError("Error: unknown command '" + op + "'");
             return;
         }
     }
 
+    private void runSurfaceTrace() throws Exception {
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out);
+        mWm.enableSurfaceTrace(pfd);
+
+        try {
+            // No one is going to wake us up, we are just waiting on SIGINT. Otherwise
+            // the WM can happily continue writing to our stdout.
+            synchronized (this) {
+                this.wait();
+            }
+        } finally {
+            mWm.disableSurfaceTrace();
+        }
+    }
+
     private void runSetScreenCapture() throws Exception {
         String userIdStr = nextArg();
         String enableStr = nextArg();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index c4eaccc..163e7d2 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -53,7 +53,7 @@
 import java.util.List;
 
 /**
- * Accessibility services are intended to assist users with disabilities in using
+ * Accessibility services should only be used to assist users with disabilities in using
  * Android devices and apps. They run in the background and receive callbacks by the system
  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
  * in the user interface, for example, the focus has changed, a button has been clicked,
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 419f723..f442ab1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -716,6 +716,11 @@
         public static boolean hasMovementAnimations(int stackId) {
             return stackId != PINNED_STACK_ID;
         }
+
+        /** Returns true if the input stack and its content can affect the device orientation. */
+        public static boolean canSpecifyOrientation(int stackId) {
+            return stackId == HOME_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID;
+        }
     }
 
     /**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c7f19a2..f798512 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -3025,6 +3025,14 @@
             reply.writeNoException();
             return true;
         }
+        case CAN_BYPASS_WORK_CHALLENGE: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final PendingIntent intent = PendingIntent.CREATOR.createFromParcel(data);
+            final boolean result = canBypassWorkChallenge(intent);
+            reply.writeNoException();
+            reply.writeInt(result ? 1 : 0);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3234,7 +3242,7 @@
             data.writeInt(0);
         }
         data.writeInt(userId);
-        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
+        mRemote.transact(START_ACTIVITY_WITH_CONFIG_TRANSACTION, data, reply, 0);
         reply.readException();
         int result = reply.readInt();
         reply.recycle();
@@ -5600,7 +5608,7 @@
         data.writeInt(mode);
         data.writeInt(sourceUserId);
         data.writeInt(targetUserId);
-        mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        mRemote.transact(GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5620,7 +5628,7 @@
         }
         data.writeInt(mode);
         data.writeInt(userId);
-        mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        mRemote.transact(REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -7109,6 +7117,20 @@
         reply.recycle();
         return;
     }
+    @Override
+    public boolean canBypassWorkChallenge(PendingIntent intent)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        intent.writeToParcel(data, 0);
+        mRemote.transact(CAN_BYPASS_WORK_CHALLENGE, data, reply, 0);
+        reply.readException();
+        final int result = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return result != 0;
+    }
 
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 387a1f5..a0408c9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -435,8 +435,10 @@
     static final class NewIntentData {
         List<ReferrerIntent> intents;
         IBinder token;
+        boolean andPause;
         public String toString() {
-            return "NewIntentData{intents=" + intents + " token=" + token + "}";
+            return "NewIntentData{intents=" + intents + " token=" + token
+                    + " andPause=" + andPause +"}";
         }
     }
 
@@ -754,10 +756,12 @@
                     configChanges, notResumed, config, overrideConfig, true, preserveWindow);
         }
 
-        public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) {
+        public final void scheduleNewIntent(
+                List<ReferrerIntent> intents, IBinder token, boolean andPause) {
             NewIntentData data = new NewIntentData();
             data.intents = intents;
             data.token = token;
+            data.andPause = andPause;
 
             sendMessage(H.NEW_INTENT, data);
         }
@@ -2797,24 +2801,34 @@
         }
     }
 
-    public final void performNewIntents(IBinder token, List<ReferrerIntent> intents) {
-        ActivityClientRecord r = mActivities.get(token);
-        if (r != null) {
-            final boolean resumed = !r.paused;
-            if (resumed) {
-                r.activity.mTemporaryPause = true;
-                mInstrumentation.callActivityOnPause(r.activity);
-            }
-            deliverNewIntents(r, intents);
-            if (resumed) {
-                r.activity.performResume();
-                r.activity.mTemporaryPause = false;
-            }
+    void performNewIntents(IBinder token, List<ReferrerIntent> intents, boolean andPause) {
+        final ActivityClientRecord r = mActivities.get(token);
+        if (r == null) {
+            return;
+        }
+
+        final boolean resumed = !r.paused;
+        if (resumed) {
+            r.activity.mTemporaryPause = true;
+            mInstrumentation.callActivityOnPause(r.activity);
+        }
+        deliverNewIntents(r, intents);
+        if (resumed) {
+            r.activity.performResume();
+            r.activity.mTemporaryPause = false;
+        }
+
+        if (r.paused && andPause) {
+            // In this case the activity was in the paused state when we delivered the intent,
+            // to guarantee onResume gets called after onNewIntent we temporarily resume the
+            // activity and pause again as the caller wanted.
+            performResumeActivity(token, false, "performNewIntents");
+            performPauseActivityIfNeeded(r, "performNewIntents");
         }
     }
 
     private void handleNewIntent(NewIntentData data) {
-        performNewIntents(data.token, data.intents);
+        performNewIntents(data.token, data.intents, data.andPause);
     }
 
     public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index e41c841..12e527e 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -190,7 +190,8 @@
             data.enforceInterface(IApplicationThread.descriptor);
             List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR);
             IBinder b = data.readStrongBinder();
-            scheduleNewIntent(pi, b);
+            final boolean andPause = data.readInt() == 1;
+            scheduleNewIntent(pi, b, andPause);
             return true;
         }
 
@@ -916,12 +917,13 @@
         data.recycle();
     }
 
-    public void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token)
+    public void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token, boolean andPause)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeTypedList(intents);
         data.writeStrongBinder(token);
+        data.writeInt(andPause ? 1 : 0);
         mRemote.transact(SCHEDULE_NEW_INTENT_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 03fcf47..462f66f 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1112,7 +1112,7 @@
             if (cursor.moveToFirst()) {
                 int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
                 if (DownloadManager.STATUS_SUCCESSFUL == status) {
-                    return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, id);
+                    return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
                 }
             }
         } finally {
@@ -1448,7 +1448,7 @@
      * @hide
      */
     public Uri getDownloadUri(long id) {
-        return ContentUris.withAppendedId(mBaseUri, id);
+        return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
     }
 
     /**
@@ -1552,7 +1552,7 @@
 
             // return content URI for cache download
             long downloadId = getLong(getColumnIndex(Downloads.Impl._ID));
-            return ContentUris.withAppendedId(mBaseUri, downloadId).toString();
+            return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, downloadId).toString();
         }
 
         private long getReason(int status) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index dfeea57..096f0cb 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -265,8 +265,20 @@
     public void finishInstrumentation(IApplicationThread target,
             int resultCode, Bundle results) throws RemoteException;
 
+    /**
+     * @return A copy of global {@link Configuration}, contains general settings for the entire
+     *         system. Corresponds to the configuration of the default display.
+     * @throws RemoteException
+     */
     public Configuration getConfiguration() throws RemoteException;
+
+    /**
+     * Updates global configuration and applies changes to the entire system.
+     * @param values Update values for global configuration.
+     * @throws RemoteException
+     */
     public void updateConfiguration(Configuration values) throws RemoteException;
+
     public void setRequestedOrientation(IBinder token,
             int requestedOrientation) throws RemoteException;
     public int getRequestedOrientation(IBinder token) throws RemoteException;
@@ -672,6 +684,18 @@
      */
     public void setHasTopUi(boolean hasTopUi) throws RemoteException;
 
+    /**
+     * Returns if the target of the PendingIntent can be fired directly, without triggering
+     * a work profile challenge. This can happen if the PendingIntent is to start direct-boot
+     * aware activities, and the target user is in RUNNING_LOCKED state, i.e. we should allow
+     * direct-boot aware activity to bypass work challenge when the user hasn't unlocked yet.
+     * @param intent the {@link  PendingIntent} to be tested.
+     * @return {@code true} if the intent should not trigger a work challenge, {@code false}
+     *     otherwise.
+     * @throws RemoteException
+     */
+    public boolean canBypassWorkChallenge(PendingIntent intent) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -1062,6 +1086,7 @@
     int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377;
     int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378;
     int SET_HAS_TOP_UI = IBinder.FIRST_CALL_TRANSACTION + 379;
+    int CAN_BYPASS_WORK_CHALLENGE = IBinder.FIRST_CALL_TRANSACTION + 380;
 
     // Start of O transactions
     int REQUEST_ACTIVITY_RELAUNCH = IBinder.FIRST_CALL_TRANSACTION+400;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index ac8b1c4..4189dd9 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -67,7 +67,8 @@
             List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
             Configuration config, Configuration overrideConfig, boolean preserveWindow)
             throws RemoteException;
-    void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException;
+    void scheduleNewIntent(
+            List<ReferrerIntent> intent, IBinder token, boolean andPause) throws RemoteException;
     void scheduleDestroyActivity(IBinder token, boolean finished,
             int configChanges) throws RemoteException;
     void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ee80ec3..28224e8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -19,6 +19,7 @@
 
 import android.app.ITransientNotification;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -57,6 +58,15 @@
     int getImportance(String pkg, int uid);
     int getPackageImportance(String pkg);
 
+    void createNotificationChannel(String pkg, in NotificationChannel channel);
+    void updateNotificationChannel(String pkg, in NotificationChannel channel);
+    void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
+    NotificationChannel getNotificationChannel(String pkg, String channelId);
+    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId);
+    void deleteNotificationChannel(String pkg, String channelId);
+    ParceledListSlice getNotificationChannels(String pkg);
+    ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid);
+
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
     StatusBarNotification[] getActiveNotifications(String callingPkg);
@@ -70,6 +80,8 @@
 
     void requestBindListener(in ComponentName component);
     void requestUnbindListener(in INotificationListener token);
+    void requestBindProvider(in ComponentName component);
+    void requestUnbindProvider(in IConditionProvider token);
 
     void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
 
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 2a1e3c2..3b273bc 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -314,7 +314,7 @@
                     ArrayList<ReferrerIntent> intents = new ArrayList<>(1);
                     intents.add(new ReferrerIntent(intent, mParent.getPackageName()));
                     if (localLOGV) Log.v(TAG, r.id + ": new intent");
-                    mActivityThread.performNewIntents(r, intents);
+                    mActivityThread.performNewIntents(r, intents, false /* andPause */);
                     r.intent = intent;
                     moveToState(r, mCurState);
                     if (mSingleMode) {
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
index 3f1d113..9d8129c 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,4 +17,3 @@
 package android.app;
 
 parcelable Notification;
-parcelable Notification.Topic;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2028572..680e518 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -992,6 +992,8 @@
     private Icon mSmallIcon;
     private Icon mLargeIcon;
 
+    private String mChannelId;
+
     /**
      * Structure to encapsulate a named action that can be shown as part of this notification.
      * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
@@ -1231,7 +1233,7 @@
                     getIcon(),
                     title,
                     actionIntent, // safe to alias
-                    new Bundle(mExtras),
+                    mExtras == null ? new Bundle() : new Bundle(mExtras),
                     getRemoteInputs(),
                     getAllowGeneratedReplies());
         }
@@ -1675,6 +1677,10 @@
         }
 
         color = parcel.readInt();
+
+        if (parcel.readInt() != 0) {
+            mChannelId = parcel.readString();
+        }
     }
 
     @Override
@@ -1780,6 +1786,8 @@
 
         that.color = this.color;
 
+        that.mChannelId = this.mChannelId;
+
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
         }
@@ -2028,6 +2036,13 @@
         }
 
         parcel.writeInt(color);
+
+        if (mChannelId != null) {
+            parcel.writeInt(1);
+            parcel.writeString(mChannelId);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     /**
@@ -2218,6 +2233,13 @@
     }
 
     /**
+     * Returns the id of the channel this notification posts to.
+     */
+    public String getNotificationChannel() {
+        return mChannelId;
+    }
+
+    /**
      * The small icon representing this notification in the status bar and content view.
      *
      * @return the small icon representing this notification.
@@ -2406,6 +2428,14 @@
         }
 
         /**
+         * Specifies the channel the notification should be delivered on.
+         */
+        public Builder setChannel(String channelId) {
+            mN.mChannelId = channelId;
+            return this;
+        }
+
+        /**
          * Add a timestamp pertaining to the notification (usually the time the event occurred).
          *
          * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
@@ -2887,8 +2917,7 @@
         }
 
         /**
-         * Make this notification automatically dismissed when the user touches it. The
-         * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
+         * Make this notification automatically dismissed when the user touches it.
          *
          * @see Notification#FLAG_AUTO_CANCEL
          */
@@ -2988,7 +3017,8 @@
         /**
          * Set this notification to be the group summary for a group of notifications.
          * Grouped notifications may display in a cluster or stack on devices which
-         * support such rendering. Requires a group key also be set using {@link #setGroup}.
+         * support such rendering. If thereRequires a group key also be set using {@link #setGroup}.
+         * The group summary may be suppressed if too few notifications are included in the group.
          * @param isGroupSummary Whether this notification should be a group summary.
          * @return this object for method chaining
          */
@@ -5834,6 +5864,7 @@
         private static final String KEY_GRAVITY = "gravity";
         private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
         private static final String KEY_DISMISSAL_ID = "dismissalId";
+        private static final String KEY_BRIDGE_TAG = "bridgeTag";
 
         // Flags bitwise-ored to mFlags
         private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
@@ -5863,6 +5894,7 @@
         private int mGravity = DEFAULT_GRAVITY;
         private int mHintScreenTimeout;
         private String mDismissalId;
+        private String mBridgeTag;
 
         /**
          * Create a {@link android.app.Notification.WearableExtender} with default
@@ -5900,6 +5932,7 @@
                 mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
                 mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
                 mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
+                mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
             }
         }
 
@@ -5953,6 +5986,9 @@
             if (mDismissalId != null) {
                 wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
             }
+            if (mBridgeTag != null) {
+                wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
+            }
 
             builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
             return builder;
@@ -5974,6 +6010,7 @@
             that.mGravity = this.mGravity;
             that.mHintScreenTimeout = this.mHintScreenTimeout;
             that.mDismissalId = this.mDismissalId;
+            that.mBridgeTag = this.mBridgeTag;
             return that;
         }
 
@@ -6462,12 +6499,11 @@
         }
 
         /**
-         * When you post a notification, if you set the dismissal id field, then when that
-         * notification is canceled, notifications on other wearables and the paired Android phone
-         * having that same dismissal id will also be canceled.  Note that this only works if you
-         * have notification bridge mode set to NO_BRIDGING in your Wear app manifest.  See
+         * Sets the dismissal id for this notification. If a notification is posted with a
+         * dismissal id, then when that notification is canceled, notifications on other wearables
+         * and the paired Android phone having that same dismissal id will also be canceled. See
          * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
-         * Notifications</a> for more information on how to use the bridge mode feature.
+         * Notifications</a> for more information.
          * @param dismissalId the dismissal id of the notification.
          * @return this object for method chaining
          */
@@ -6484,6 +6520,27 @@
             return mDismissalId;
         }
 
+        /**
+         * Sets a bridge tag for this notification. A bridge tag can be set for notifications
+         * posted from a phone to provide finer-grained control on what notifications are bridged
+         * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
+         * Features to Notifications</a> for more information.
+         * @param bridgeTag the bridge tag of the notification.
+         * @return this object for method chaining
+         */
+        public WearableExtender setBridgeTag(String bridgeTag) {
+            mBridgeTag = bridgeTag;
+            return this;
+        }
+
+        /**
+         * Returns the bridge tag of the notification.
+         * @return the bridge tag or null if not present.
+         */
+        public String getBridgeTag() {
+            return mBridgeTag;
+        }
+
         private void setFlag(int mask, boolean value) {
             if (value) {
                 mFlags |= mask;
diff --git a/core/java/android/app/NotificationChannel.aidl b/core/java/android/app/NotificationChannel.aidl
new file mode 100644
index 0000000..53e6863
--- /dev/null
+++ b/core/java/android/app/NotificationChannel.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.app;
+
+parcelable NotificationChannel;
\ No newline at end of file
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
new file mode 100644
index 0000000..530b8bb
--- /dev/null
+++ b/core/java/android/app/NotificationChannel.java
@@ -0,0 +1,382 @@
+package android.app;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.notification.NotificationListenerService;
+import android.text.TextUtils;
+
+import java.io.IOException;
+
+/**
+ * A representation of settings that apply to a collection of similarly themed notifications.
+ */
+public final class NotificationChannel implements Parcelable {
+
+    /**
+     * The id of the default channel for an app. All notifications posted without a notification
+     * channel specified are posted to this channel.
+     */
+    public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+
+    private static final String TAG_CHANNEL = "channel";
+    private static final String ATT_NAME = "name";
+    private static final String ATT_ID = "id";
+    private static final String ATT_PRIORITY = "priority";
+    private static final String ATT_VISIBILITY = "visibility";
+    private static final String ATT_IMPORTANCE = "importance";
+    private static final String ATT_LIGHTS = "lights";
+    private static final String ATT_VIBRATION = "vibration";
+    private static final String ATT_DEFAULT_RINGTONE = "ringtone";
+
+    private static final int DEFAULT_VISIBILITY =
+            NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+    private static final int DEFAULT_IMPORTANCE =
+            NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+
+    private final String mId;
+    private CharSequence mName;
+    private int mImportance = DEFAULT_IMPORTANCE;
+    private boolean mBypassDnd;
+    private int mLockscreenVisibility = DEFAULT_VISIBILITY;
+    private Uri mRingtone;
+    private boolean mLights;
+    private boolean mVibration;
+
+    /**
+     * Creates a notification channel.
+     *
+     * @param id The id of the channel. Must be unique per package.
+     * @param name The user visible name of the channel.
+     */
+    public NotificationChannel(String id, CharSequence name) {
+        this.mId = id;
+        this.mName = name;
+    }
+
+    protected NotificationChannel(Parcel in) {
+        if (in.readByte() != 0) {
+            mId = in.readString();
+        } else {
+            mId = null;
+        }
+        mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mImportance = in.readInt();
+        mBypassDnd = in.readByte() != 0;
+        mLockscreenVisibility = in.readInt();
+        if (in.readByte() != 0) {
+            mRingtone = Uri.CREATOR.createFromParcel(in);
+        } else {
+            mRingtone = null;
+        }
+        mLights = in.readByte() != 0;
+        mVibration = in.readByte() != 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mId != null) {
+            dest.writeByte((byte) 1);
+            dest.writeString(mId);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        TextUtils.writeToParcel(mName, dest, flags);
+        dest.writeInt(mImportance);
+        dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
+        dest.writeInt(mLockscreenVisibility);
+        if (mRingtone != null) {
+            dest.writeByte((byte) 1);
+            mRingtone.writeToParcel(dest, 0);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        dest.writeByte(mLights ? (byte) 1 : (byte) 0);
+        dest.writeByte(mVibration ? (byte) 1 : (byte) 0);
+    }
+
+    // Only modifiable by users.
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void setName(CharSequence name) {
+        this.mName = name;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void setImportance(int importance) {
+        this.mImportance = importance;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void setBypassDnd(boolean bypassDnd) {
+        this.mBypassDnd = bypassDnd;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void setLockscreenVisibility(int lockscreenVisibility) {
+        this.mLockscreenVisibility = lockscreenVisibility;
+    }
+
+    // Modifiable by apps.
+
+    /**
+     * Sets the ringtone that should be played for notifications posted to this channel if
+     * the notifications don't supply a ringtone.
+     */
+    public void setDefaultRingtone(Uri defaultRingtone) {
+        this.mRingtone = defaultRingtone;
+    }
+
+    /**
+     * Sets whether notifications posted to this channel should display notification lights,
+     * on devices that support that feature.
+     */
+    public void setLights(boolean lights) {
+        this.mLights = lights;
+    }
+
+    /**
+     * Sets whether notification posted to this channel should vibrate, even if individual
+     * notifications are marked as having vibration.
+     */
+    public void setVibration(boolean vibration) {
+        this.mVibration = vibration;
+    }
+
+    /**
+     * Returns the id of this channel.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the user visible name of this channel.
+     */
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the user specified importance {e.g. @link NotificationManager#IMPORTANCE_LOW} for
+     * notifications posted to this channel.
+     */
+    public int getImportance() {
+        return mImportance;
+    }
+
+    /**
+     * Whether or not notifications posted to this channel can bypass the Do Not Disturb
+     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
+     */
+    public boolean canBypassDnd() {
+        return mBypassDnd;
+    }
+
+    /**
+     * Returns the notification sound for this channel.
+     */
+    public Uri getDefaultRingtone() {
+        return mRingtone;
+    }
+
+    /**
+     * Returns whether notifications posted to this channel trigger notification lights.
+     */
+    public boolean shouldShowLights() {
+        return mLights;
+    }
+
+    /**
+     * Returns whether notifications posted to this channel always vibrate.
+     */
+    public boolean shouldVibrate() {
+        return mVibration;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public int getLockscreenVisibility() {
+        return mLockscreenVisibility;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void populateFromXml(XmlPullParser parser) {
+        // Name and id are set in the constructor.
+        setImportance(safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE));
+        setBypassDnd(Notification.PRIORITY_DEFAULT
+                != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
+        setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
+        setDefaultRingtone(safeUri(parser, ATT_DEFAULT_RINGTONE));
+        setLights(safeBool(parser, ATT_LIGHTS, false));
+        setVibration(safeBool(parser, ATT_VIBRATION, false));
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void writeXml(XmlSerializer out) throws IOException {
+        out.startTag(null, TAG_CHANNEL);
+        out.attribute(null, ATT_ID, getId());
+        out.attribute(null, ATT_NAME, getName().toString());
+        if (getImportance() != DEFAULT_IMPORTANCE) {
+            out.attribute(
+                    null, ATT_IMPORTANCE, Integer.toString(getImportance()));
+        }
+        if (canBypassDnd()) {
+            out.attribute(
+                    null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX));
+        }
+        if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
+            out.attribute(null, ATT_VISIBILITY,
+                    Integer.toString(getLockscreenVisibility()));
+        }
+        if (getDefaultRingtone() != null) {
+            out.attribute(null, ATT_DEFAULT_RINGTONE, getDefaultRingtone().toString());
+        }
+        if (shouldShowLights()) {
+            out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights()));
+        }
+        if (shouldVibrate()) {
+            out.attribute(null, ATT_VIBRATION, Boolean.toString(shouldVibrate()));
+        }
+        out.endTag(null, TAG_CHANNEL);
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public JSONObject toJson() throws JSONException {
+        JSONObject record = new JSONObject();
+        record.put(ATT_ID, getId());
+        record.put(ATT_NAME, getName());
+        if (getImportance() != DEFAULT_IMPORTANCE) {
+            record.put(ATT_IMPORTANCE,
+                    NotificationListenerService.Ranking.importanceToString(getImportance()));
+        }
+        if (canBypassDnd()) {
+            record.put(ATT_PRIORITY, Notification.PRIORITY_MAX);
+        }
+        if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
+            record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility()));
+        }
+        if (getDefaultRingtone() != null) {
+            record.put(ATT_DEFAULT_RINGTONE, getDefaultRingtone().toString());
+        }
+        record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights()));
+        record.put(ATT_VIBRATION, Boolean.toString(shouldVibrate()));
+
+        return record;
+    }
+
+    private static Uri safeUri(XmlPullParser parser, String att) {
+        final String val = parser.getAttributeValue(null, att);
+        return val == null ? null : Uri.parse(val);
+    }
+
+    private static int safeInt(XmlPullParser parser, String att, int defValue) {
+        final String val = parser.getAttributeValue(null, att);
+        return tryParseInt(val, defValue);
+    }
+
+    private static int tryParseInt(String value, int defValue) {
+        if (TextUtils.isEmpty(value)) return defValue;
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
+        final String value = parser.getAttributeValue(null, att);
+        if (TextUtils.isEmpty(value)) return defValue;
+        return Boolean.parseBoolean(value);
+    }
+
+    public static final Creator<NotificationChannel> CREATOR = new Creator<NotificationChannel>() {
+        @Override
+        public NotificationChannel createFromParcel(Parcel in) {
+            return new NotificationChannel(in);
+        }
+
+        @Override
+        public NotificationChannel[] newArray(int size) {
+            return new NotificationChannel[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        NotificationChannel that = (NotificationChannel) o;
+
+        if (mImportance != that.mImportance) return false;
+        if (mBypassDnd != that.mBypassDnd) return false;
+        if (mLockscreenVisibility != that.mLockscreenVisibility) return false;
+        if (mLights != that.mLights) return false;
+        if (mVibration != that.mVibration) return false;
+        if (!mId.equals(that.mId)) return false;
+        if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false;
+        return mRingtone != null ? mRingtone.equals(
+                that.mRingtone) : that.mRingtone == null;
+    }
+
+    @Override
+    public String toString() {
+        return "NotificationChannel{" +
+                "mId='" + mId + '\'' +
+                ", mName=" + mName +
+                ", mImportance=" + mImportance +
+                ", mBypassDnd=" + mBypassDnd +
+                ", mLockscreenVisibility=" + mLockscreenVisibility +
+                ", mRingtone='" + mRingtone + '\'' +
+                ", mLights=" + mLights +
+                ", mVibration=" + mVibration +
+                '}';
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mId.hashCode();
+        result = 31 * result + (mName != null ? mName.hashCode() : 0);
+        result = 31 * result + mImportance;
+        result = 31 * result + (mBypassDnd ? 1 : 0);
+        result = 31 * result + mLockscreenVisibility;
+        result = 31 * result + (mRingtone != null ? mRingtone.hashCode() : 0);
+        result = 31 * result + (mLights ? 1 : 0);
+        result = 31 * result + (mVibration ? 1 : 0);
+        return result;
+    }
+}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index ff514bd..39cd2b5 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -378,6 +378,66 @@
     }
 
     /**
+     * Creates a notification channel that notifications can be posted to.
+     */
+    public void createNotificationChannel(NotificationChannel channel) {
+        INotificationManager service = getService();
+        try {
+            service.createNotificationChannel(mContext.getPackageName(), channel);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the notification channel settings for a given channel id.
+     */
+    public NotificationChannel getNotificationChannel(String channelId) {
+        INotificationManager service = getService();
+        try {
+            return service.getNotificationChannel(mContext.getPackageName(), channelId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns all notification channels created by the calling app.
+     */
+    public List<NotificationChannel> getNotificationChannels() {
+        INotificationManager service = getService();
+        try {
+            return service.getNotificationChannels(mContext.getPackageName()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Updates settings for a given channel.
+     */
+    public void updateNotificationChannel(NotificationChannel channel) {
+        INotificationManager service = getService();
+        try {
+            service.updateNotificationChannel(mContext.getPackageName(), channel);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deletes the given notification channel.
+     */
+    public void deleteNotificationChannel(String channelId) {
+        INotificationManager service = getService();
+        try {
+            service.deleteNotificationChannel(mContext.getPackageName(), channelId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide
      */
     public ComponentName getEffectsSuppressor() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c559943..540678e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -411,6 +411,14 @@
     public static final int NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED = 3;
 
     /**
+     * Default and maximum timeout in milliseconds after which unlocking with weak auth times out,
+     * i.e. the user has to use a strong authentication method like password, PIN or pattern.
+     *
+     * @hide
+     */
+    public static final long DEFAULT_STRONG_AUTH_TIMEOUT_MS = 72 * 60 * 60 * 1000; // 72h
+
+    /**
      * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
      * allows a mobile device management application or NFC programmer application which starts
      * managed provisioning to pass data to the management application instance after provisioning.
@@ -2330,6 +2338,78 @@
     }
 
     /**
+     * Called by a device/profile owner to set the timeout after which unlocking with secondary, non
+     * strong auth (e.g. fingerprint, trust agents) times out, i.e. the user has to use a strong
+     * authentication method like password, pin or pattern.
+     *
+     * <p>This timeout is used internally to reset the timer to require strong auth again after
+     * specified timeout each time it has been successfully used.
+     *
+     * <p>Fingerprint can also be disabled altogether using {@link #KEYGUARD_DISABLE_FINGERPRINT}.
+     *
+     * <p>Trust agents can also be disabled altogether using {@link #KEYGUARD_DISABLE_TRUST_AGENTS}.
+     *
+     * <p>The calling device admin must be a device or profile owner. If it is not,
+     * a {@link SecurityException} will be thrown.
+     *
+     * <p>This method can be called on the {@link DevicePolicyManager} instance returned by
+     * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
+     * profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param timeoutMs The new timeout, after which the user will have to unlock with strong
+     *         authentication method. If the timeout is lower than 1 hour (minimum) or higher than
+     *         72 hours (default and maximum) an {@link IllegalArgumentException} is thrown.
+     *
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws IllegalArgumentException if the timeout is lower than 1 hour (minimum) or higher than
+     *         72 hours (default and maximum)
+     *
+     * @hide
+     */
+    public void setRequiredStrongAuthTimeout(@NonNull ComponentName admin,
+            long timeoutMs) {
+        if (mService != null) {
+            try {
+                mService.setRequiredStrongAuthTimeout(admin, timeoutMs, mParentInstance);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Determine for how long the user will be able to use secondary, non strong auth for
+     * authentication, since last strong method authentication (password, pin or pattern) was used.
+     * After the returned timeout the user is required to use strong authentication method.
+     *
+     * <p>This method can be called on the {@link DevicePolicyManager} instance
+     * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
+     * restrictions on the parent profile.
+     *
+     * @param admin The name of the admin component to check, or {@code null} to aggregate
+     *         accross all participating admins.
+     * @return The timeout or default timeout if not configured
+     *
+     * @hide
+     */
+    public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin) {
+        return getRequiredStrongAuthTimeout(admin, myUserId());
+    }
+
+    /** @hide per-user version */
+    public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin, @UserIdInt int userId) {
+        if (mService != null) {
+            try {
+                return mService.getRequiredStrongAuthTimeout(admin, userId, mParentInstance);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+    }
+
+    /**
      * Make the device lock immediately, as if the lock screen timeout has expired at the point of
      * this call.
      * <p>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f428c77..8c376bb 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -82,6 +82,9 @@
     long getMaximumTimeToLock(in ComponentName who, int userHandle, boolean parent);
     long getMaximumTimeToLockForUserAndProfiles(int userHandle);
 
+    void setRequiredStrongAuthTimeout(in ComponentName who, long timeMs, boolean parent);
+    long getRequiredStrongAuthTimeout(in ComponentName who, int userId, boolean parent);
+
     void lockNow(boolean parent);
 
     void wipeData(int flags);
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 444e429..a395aa4 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -66,21 +66,6 @@
         "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
 
     /**
-     * Intent used to broadcast the change in metadata state of playing track on the AVRCP
-     * AG.
-     *
-     * <p>This intent will have the two extras:
-     * <ul>
-     *    <li> {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.</li>
-     *    <li> {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback
-     *    state. </li>
-     * </ul>
-     */
-    public static final String ACTION_TRACK_EVENT =
-        "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT";
-
-
-    /**
      * Intent used to broadcast the change in player application setting state on AVRCP AG.
      *
      * <p>This intent will have the following extras:
@@ -92,35 +77,9 @@
     public static final String ACTION_PLAYER_SETTING =
         "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
 
-    public static final String EXTRA_METADATA =
-            "android.bluetooth.avrcp-controller.profile.extra.METADATA";
-
-    public static final String EXTRA_PLAYBACK =
-            "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";
-
     public static final String EXTRA_PLAYER_SETTING =
             "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
 
-    /*
-     * KeyCoded for Pass Through Commands
-     */
-    public static final int PASS_THRU_CMD_ID_PLAY = 0x44;
-    public static final int PASS_THRU_CMD_ID_PAUSE = 0x46;
-    public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41;
-    public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42;
-    public static final int PASS_THRU_CMD_ID_STOP = 0x45;
-    public static final int PASS_THRU_CMD_ID_FF = 0x49;
-    public static final int PASS_THRU_CMD_ID_REWIND = 0x48;
-    public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B;
-    public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C;
-    /* Key State Variables */
-    public static final int KEY_STATE_PRESSED = 0;
-    public static final int KEY_STATE_RELEASED = 1;
-    /* Group Navigation Key Codes */
-    public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00;
-    public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01;
-
-
     private Context mContext;
     private ServiceListener mServiceListener;
     private IBluetoothAvrcpController mService;
@@ -267,20 +226,6 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
-    public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
-        if (DBG) Log.d(TAG, "sendPassThroughCmd");
-        if (mService != null && isEnabled()) {
-            try {
-                mService.sendPassThroughCmd(device, keyCode, keyState);
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e);
-                return;
-            }
-        }
-        if (mService == null) Log.w(TAG, "Proxy not attached to service");
-    }
-
     /**
      * Gets the player application settings.
      *
@@ -301,49 +246,6 @@
     }
 
     /**
-     * Gets the metadata for the current track.
-     *
-     * This should be usually called when application UI needs to be updated, eg. when the track
-     * changes or immediately after connecting and getting the current state.
-     * @return the {@link MediaMetadata} or {@link null} if there is an error.
-     */
-    public MediaMetadata getMetadata(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getMetadata");
-        MediaMetadata metadata = null;
-        if (mService != null && isEnabled()) {
-            try {
-                metadata = mService.getMetadata(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
-                return null;
-            }
-        }
-        return metadata;
-    }
-
-    /**
-     * Gets the playback state for current track.
-     *
-     * When the application is first connecting it can use current track state to get playback info.
-     * For all further updates it should listen to notifications.
-     * @return the {@link PlaybackState} or {@link null} if there is an error.
-     */
-    public PlaybackState getPlaybackState(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getPlaybackState");
-        PlaybackState playbackState = null;
-        if (mService != null && isEnabled()) {
-            try {
-                playbackState = mService.getPlaybackState(device);
-            } catch (RemoteException e) {
-                Log.e(TAG,
-                    "Error talking to BT service in getPlaybackState() " + e);
-                return null;
-            }
-        }
-        return playbackState;
-    }
-
-    /**
      * Sets the player app setting for current player.
      * returns true in case setting is supported by remote, false otherwise
      */
diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
index f1288d0..cfa11ca 100644
--- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
+++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
@@ -30,10 +30,7 @@
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
-    void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState);
     BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device);
-    MediaMetadata getMetadata(in BluetoothDevice device);
-    PlaybackState getPlaybackState(in BluetoothDevice device);
     boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting);
     void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState);
 }
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 22ab43b..d07b545 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -265,6 +265,7 @@
     public static final String SCHEME_HTTPS = "https";
 
     private int mPriority;
+    private int mOrder;
     private final ArrayList<String> mActions;
     private ArrayList<String> mCategories = null;
     private ArrayList<String> mDataSchemes = null;
@@ -425,6 +426,7 @@
      */
     public IntentFilter(IntentFilter o) {
         mPriority = o.mPriority;
+        mOrder = o.mOrder;
         mActions = new ArrayList<String>(o.mActions);
         if (o.mCategories != null) {
             mCategories = new ArrayList<String>(o.mCategories);
@@ -477,6 +479,16 @@
         return mPriority;
     }
 
+    /** @hide */
+    public final void setOrder(int order) {
+        mOrder = order;
+    }
+
+    /** @hide */
+    public final int getOrder() {
+        return mOrder;
+    }
+
     /**
      * Set whether this filter will needs to be automatically verified against its data URIs or not.
      * The default is false.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 94c406a..5d2c6cd 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -377,6 +377,7 @@
 
     /** @hide */
     @IntDef({
+            SCREEN_ORIENTATION_UNSET,
             SCREEN_ORIENTATION_UNSPECIFIED,
             SCREEN_ORIENTATION_LANDSCAPE,
             SCREEN_ORIENTATION_PORTRAIT,
@@ -398,6 +399,15 @@
     public @interface ScreenOrientation {}
 
     /**
+     * Internal constant used to indicate that the app didn't set a specific orientation value.
+     * Different from {@link #SCREEN_ORIENTATION_UNSPECIFIED} below as the app can set its
+     * orientation to {@link #SCREEN_ORIENTATION_UNSPECIFIED} while this means that the app didn't
+     * set anything. The system will mostly treat this similar to
+     * {@link #SCREEN_ORIENTATION_UNSPECIFIED}.
+     * @hide
+     */
+    public static final int SCREEN_ORIENTATION_UNSET = -2;
+    /**
      * Constant corresponding to <code>unspecified</code> in
      * the {@link android.R.attr#screenOrientation} attribute.
      */
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index a110383..aea843a 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -390,15 +390,17 @@
     @VisibleForTesting
     protected boolean inSystemImage(int callerUid) {
         String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
-        for (String name : packages) {
-            try {
-                PackageInfo packageInfo =
-                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
-                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    return true;
+        if (packages != null) {
+            for (String name : packages) {
+                try {
+                    PackageInfo packageInfo =
+                            mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
+                    if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    return false;
                 }
-            } catch (PackageManager.NameNotFoundException e) {
-                return false;
             }
         }
         return false;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 35d3dc3..ee1f190 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1139,78 +1139,88 @@
      * PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
      */
     public int diff(Configuration delta) {
+        return diff(delta, false /* compareUndefined */);
+    }
+
+    /**
+     * Variation of {@link #diff(Configuration)} with an option to skip checks for undefined values.
+     *
+     * @hide
+     */
+    public int diff(Configuration delta, boolean compareUndefined) {
         int changed = 0;
-        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+        if ((compareUndefined || delta.fontScale > 0) && fontScale != delta.fontScale) {
             changed |= ActivityInfo.CONFIG_FONT_SCALE;
         }
-        if (delta.mcc != 0 && mcc != delta.mcc) {
+        if ((compareUndefined || delta.mcc != 0) && mcc != delta.mcc) {
             changed |= ActivityInfo.CONFIG_MCC;
         }
-        if (delta.mnc != 0 && mnc != delta.mnc) {
+        if ((compareUndefined || delta.mnc != 0) && mnc != delta.mnc) {
             changed |= ActivityInfo.CONFIG_MNC;
         }
         fixUpLocaleList();
         delta.fixUpLocaleList();
-        if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
+        if ((compareUndefined || !delta.mLocaleList.isEmpty())
+                && !mLocaleList.equals(delta.mLocaleList)) {
             changed |= ActivityInfo.CONFIG_LOCALE;
             changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
         }
         final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
-        if (deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED &&
-                deltaScreenLayoutDir != (screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
+        if ((compareUndefined || deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED)
+                && deltaScreenLayoutDir != (screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
             changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
         }
-        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
+        if ((compareUndefined || delta.touchscreen != TOUCHSCREEN_UNDEFINED)
                 && touchscreen != delta.touchscreen) {
             changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
         }
-        if (delta.keyboard != KEYBOARD_UNDEFINED
+        if ((compareUndefined || delta.keyboard != KEYBOARD_UNDEFINED)
                 && keyboard != delta.keyboard) {
             changed |= ActivityInfo.CONFIG_KEYBOARD;
         }
-        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
+        if ((compareUndefined || delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED)
                 && keyboardHidden != delta.keyboardHidden) {
             changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
         }
-        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
+        if ((compareUndefined || delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED)
                 && hardKeyboardHidden != delta.hardKeyboardHidden) {
             changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
         }
-        if (delta.navigation != NAVIGATION_UNDEFINED
+        if ((compareUndefined || delta.navigation != NAVIGATION_UNDEFINED)
                 && navigation != delta.navigation) {
             changed |= ActivityInfo.CONFIG_NAVIGATION;
         }
-        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
+        if ((compareUndefined || delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED)
                 && navigationHidden != delta.navigationHidden) {
             changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
         }
-        if (delta.orientation != ORIENTATION_UNDEFINED
+        if ((compareUndefined || delta.orientation != ORIENTATION_UNDEFINED)
                 && orientation != delta.orientation) {
             changed |= ActivityInfo.CONFIG_ORIENTATION;
         }
-        if (getScreenLayoutNoDirection(delta.screenLayout) !=
-                    (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+        if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) !=
+                (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED))
                 && getScreenLayoutNoDirection(screenLayout) !=
-                    getScreenLayoutNoDirection(delta.screenLayout)) {
+                getScreenLayoutNoDirection(delta.screenLayout)) {
             changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
         }
-        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
+        if ((compareUndefined || delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED))
                 && uiMode != delta.uiMode) {
             changed |= ActivityInfo.CONFIG_UI_MODE;
         }
-        if (delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED
+        if ((compareUndefined || delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED)
                 && screenWidthDp != delta.screenWidthDp) {
             changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
         }
-        if (delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED
+        if ((compareUndefined || delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED)
                 && screenHeightDp != delta.screenHeightDp) {
             changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
         }
-        if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
+        if ((compareUndefined || delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED)
                 && smallestScreenWidthDp != delta.smallestScreenWidthDp) {
             changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
         }
-        if (delta.densityDpi != DENSITY_DPI_UNDEFINED
+        if ((compareUndefined || delta.densityDpi != DENSITY_DPI_UNDEFINED)
                 && densityDpi != delta.densityDpi) {
             changed |= ActivityInfo.CONFIG_DENSITY;
         }
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 9478dc0..45fa15e 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -27,6 +27,7 @@
 import android.media.ExifInterface;
 import android.media.Image;
 import android.os.SystemClock;
+import android.util.Log;
 import android.util.Size;
 
 import java.io.IOException;
@@ -89,17 +90,33 @@
             throw new IllegalArgumentException("Null argument to DngCreator constructor");
         }
 
-        // Find current time
+        // Find current time in milliseconds since 1970
         long currentTime = System.currentTimeMillis();
+        // Assume that sensor timestamp has that timebase to start
+        long timeOffset = 0;
 
-        // Find boot time
-        long bootTimeMillis = currentTime - SystemClock.elapsedRealtime();
+        int timestampSource = characteristics.get(
+                CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
+
+        if (timestampSource == CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME) {
+            // This means the same timebase as SystemClock.elapsedRealtime(),
+            // which is CLOCK_BOOTTIME
+            timeOffset = currentTime - SystemClock.elapsedRealtime();
+        } else if (timestampSource == CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN) {
+            // This means the same timebase as System.currentTimeMillis(),
+            // which is CLOCK_MONOTONIC
+            timeOffset = currentTime - SystemClock.uptimeMillis();
+        } else {
+            // Unexpected time source - treat as CLOCK_MONOTONIC
+            Log.w(TAG, "Sensor timestamp source is unexpected: " + timestampSource);
+            timeOffset = currentTime - SystemClock.uptimeMillis();
+        }
 
         // Find capture time (nanos since boot)
         Long timestamp = metadata.get(CaptureResult.SENSOR_TIMESTAMP);
         long captureTime = currentTime;
         if (timestamp != null) {
-            captureTime = timestamp / 1000000 + bootTimeMillis;
+            captureTime = timestamp / 1000000 + timeOffset;
         }
 
         // Format for metadata
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 6f39935..5a79926 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -278,7 +278,6 @@
     protected void finalize() throws Throwable {
         try {
             mCloseGuard.warnIfOpen();
-            close();
         } finally {
             super.finalize();
         }
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 6129119e..b531e5f 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -150,6 +150,11 @@
      */
     public boolean queue(ByteBuffer buffer, int length) {
         boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
+
+        // save our buffer for when the request has completed
+        mBuffer = buffer;
+        mLength = length;
+
         boolean result;
         if (buffer.isDirect()) {
             result = native_queue_direct(buffer, length, out);
@@ -158,10 +163,9 @@
         } else {
             throw new IllegalArgumentException("buffer is not direct and has no array");
         }
-        if (result) {
-            // save our buffer for when the request has completed
-            mBuffer = buffer;
-            mLength = length;
+        if (!result) {
+            mBuffer = null;
+            mLength = 0;
         }
         return result;
     }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7669635..fd9e60d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -343,6 +343,15 @@
     public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
 
     /**
+     * Action used to display a dialog that asks the user whether to avoid a network that is no
+     * longer validated. This intent is used to start the dialog in settings via startActivity.
+     *
+     * @hide
+     */
+    public static final String ACTION_PROMPT_LOST_VALIDATION =
+            "android.net.conn.PROMPT_LOST_VALIDATION";
+
+    /**
      * Invalid tethering type.
      * @see #startTethering(int, OnStartTetheringCallback, boolean)
      * @hide
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ebb9601..6196400 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -182,8 +182,15 @@
      */
     public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
 
+    /**
+     * Indicates that this network is available for use by apps, and not a network that is being
+     * kept up in the background to facilitate fast network switching.
+     * @hide
+     */
+    public static final int NET_CAPABILITY_FOREGROUND = 18;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_CAPTIVE_PORTAL;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -194,7 +201,8 @@
             // http://b/18206275
             (1 << NET_CAPABILITY_TRUSTED) |
             (1 << NET_CAPABILITY_VALIDATED) |
-            (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
+            (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
+            (1 << NET_CAPABILITY_FOREGROUND);
 
     /**
      * Network specifier for factories which want to match any network specifier
@@ -217,8 +225,7 @@
      * get immediately torn down because they do not have the requested capability.
      */
     private static final long NON_REQUESTABLE_CAPABILITIES =
-            (1 << NET_CAPABILITY_VALIDATED) |
-            (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
+            MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
 
     /**
      * Capabilities that are set by default when the object is constructed.
@@ -325,6 +332,7 @@
     public String describeFirstNonRequestableCapability() {
         if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED";
         if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL";
+        if (hasCapability(NET_CAPABILITY_FOREGROUND)) return "NET_CAPABILITY_FOREGROUND";
         // This cannot happen unless the preceding checks are incomplete.
         if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) {
             return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities);
@@ -352,6 +360,11 @@
                 (that.mNetworkCapabilities & ~MUTABLE_CAPABILITIES));
     }
 
+    private boolean equalsNetCapabilitiesRequestable(NetworkCapabilities that) {
+        return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
+                (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES));
+    }
+
     /**
      * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if all the capabilities it provides are
      * typically provided by restricted networks.
@@ -756,6 +769,19 @@
                 equalsSpecifier(nc));
     }
 
+    /**
+     * Checks that our requestable capabilities are the same as those of the given
+     * {@code NetworkCapabilities}.
+     *
+     * @hide
+     */
+    public boolean equalRequestableCapabilities(NetworkCapabilities nc) {
+        if (nc == null) return false;
+        return (equalsNetCapabilitiesRequestable(nc) &&
+                equalsTransportTypes(nc) &&
+                equalsSpecifier(nc));
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
@@ -840,6 +866,7 @@
                 case NET_CAPABILITY_NOT_VPN:        capabilities += "NOT_VPN"; break;
                 case NET_CAPABILITY_VALIDATED:      capabilities += "VALIDATED"; break;
                 case NET_CAPABILITY_CAPTIVE_PORTAL: capabilities += "CAPTIVE_PORTAL"; break;
+                case NET_CAPABILITY_FOREGROUND:     capabilities += "FOREGROUND"; break;
             }
             if (++i < types.length) capabilities += "&";
         }
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 4501f7b..ae72470 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -49,7 +49,7 @@
     public final int legacyType;
 
     /**
-     * A NetworkRequest as used by the system can be one of three types:
+     * A NetworkRequest as used by the system can be one of the following types:
      *
      *     - LISTEN, for which the framework will issue callbacks about any
      *       and all networks that match the specified NetworkCapabilities,
@@ -64,7 +64,20 @@
      *       current network (if any) that matches the capabilities of the
      *       default Internet request (mDefaultRequest), but which cannot cause
      *       the framework to either create or retain the existence of any
-     *       specific network.
+     *       specific network. Note that from the point of view of the request
+     *       matching code, TRACK_DEFAULT is identical to REQUEST: its special
+     *       behaviour is not due to different semantics, but to the fact that
+     *       the system will only ever create a TRACK_DEFAULT with capabilities
+     *       that are identical to the default request's capabilities, thus
+     *       causing it to share fate in every way with the default request.
+     *
+     *     - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
+     *       to retain the NET_CAPABILITY_FOREGROUND capability. A network with
+     *       no foreground requests is in the background. A network that has
+     *       one or more background requests and loses its last foreground
+     *       request to a higher-scoring network will not go into the
+     *       background immediately, but will linger and go into the background
+     *       after the linger timeout.
      *
      *     - The value NONE is used only by applications. When an application
      *       creates a NetworkRequest, it does not have a type; the type is set
@@ -77,7 +90,8 @@
         NONE,
         LISTEN,
         TRACK_DEFAULT,
-        REQUEST
+        REQUEST,
+        BACKGROUND_REQUEST,
     };
 
     /**
@@ -140,7 +154,7 @@
          * Add the given capability requirement to this builder.  These represent
          * the requested network's required capabilities.  Note that when searching
          * for a network to satisfy a request, all capabilities requested must be
-         * satisfied.  See {@link NetworkCapabilities} for {@code NET_CAPABILITIY_*}
+         * satisfied.  See {@link NetworkCapabilities} for {@code NET_CAPABILITY_*}
          * definitions.
          *
          * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add.
@@ -284,7 +298,7 @@
         };
 
     /**
-     * Returns true iff. the contained NetworkRequest is of type LISTEN.
+     * Returns true iff. this NetworkRequest is of type LISTEN.
      *
      * @hide
      */
@@ -298,8 +312,9 @@
      *     - should be associated with at most one satisfying network
      *       at a time;
      *
-     *     - should cause a network to be kept up if it is the best network
-     *       which can satisfy the NetworkRequest.
+     *     - should cause a network to be kept up, but not necessarily in
+     *       the foreground, if it is the best network which can satisfy the
+     *       NetworkRequest.
      *
      * For full detail of how isRequest() is used for pairing Networks with
      * NetworkRequests read rematchNetworkAndRequests().
@@ -307,9 +322,36 @@
      * @hide
      */
     public boolean isRequest() {
+        return isForegroundRequest() || isBackgroundRequest();
+    }
+
+    /**
+     * Returns true iff. the contained NetworkRequest is one that:
+     *
+     *     - should be associated with at most one satisfying network
+     *       at a time;
+     *
+     *     - should cause a network to be kept up and in the foreground if
+     *       it is the best network which can satisfy the NetworkRequest.
+     *
+     * For full detail of how isRequest() is used for pairing Networks with
+     * NetworkRequests read rematchNetworkAndRequests().
+     *
+     * @hide
+     */
+    public boolean isForegroundRequest() {
         return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
     }
 
+    /**
+     * Returns true iff. this NetworkRequest is of type BACKGROUND_REQUEST.
+     *
+     * @hide
+     */
+    public boolean isBackgroundRequest() {
+        return type == Type.BACKGROUND_REQUEST;
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/os/PatternMatcher.java b/core/java/android/os/PatternMatcher.java
index 56dc837..3890fbf 100644
--- a/core/java/android/os/PatternMatcher.java
+++ b/core/java/android/os/PatternMatcher.java
@@ -16,6 +16,10 @@
 
 package android.os;
 
+import android.util.Log;
+
+import java.util.Arrays;
+
 /**
  * A simple pattern matcher, which is safe to use on untrusted data: it does
  * not provide full reg-exp support, only simple globbing that can not be
@@ -44,13 +48,59 @@
      * wildcard part of a normal regexp. 
      */
     public static final int PATTERN_SIMPLE_GLOB = 2;
-    
+
+    /**
+     * Pattern type: the given pattern is interpreted with a regular
+     * expression-like syntax for matching against the string it is tested
+     * against. Supported tokens include dot ({@code .}) and sets ({@code [...]})
+     * with full support for character ranges and the not ({@code ^}) modifier.
+     * Supported modifiers include star ({@code *}) for zero-or-more, plus ({@code +})
+     * for one-or-more and full range ({@code {...}}) support. This is a simple
+     * evaulation implementation in which matching is done against the pattern in
+     * realtime with no backtracking support.
+     *
+     * {@hide} Pending approval for public API
+     */
+    public static final int PATTERN_ADVANCED_GLOB = 3;
+
+    // token types for advanced matching
+    private static final int TOKEN_TYPE_LITERAL = 0;
+    private static final int TOKEN_TYPE_ANY = 1;
+    private static final int TOKEN_TYPE_SET = 2;
+    private static final int TOKEN_TYPE_INVERSE_SET = 3;
+
+    // Return for no match
+    private static final int NO_MATCH = -1;
+
+    private static final String TAG = "PatternMatcher";
+
+    // Parsed placeholders for advanced patterns
+    private static final int PARSED_TOKEN_CHAR_SET_START = -1;
+    private static final int PARSED_TOKEN_CHAR_SET_INVERSE_START = -2;
+    private static final int PARSED_TOKEN_CHAR_SET_STOP = -3;
+    private static final int PARSED_TOKEN_CHAR_ANY = -4;
+    private static final int PARSED_MODIFIER_RANGE_START = -5;
+    private static final int PARSED_MODIFIER_RANGE_STOP = -6;
+    private static final int PARSED_MODIFIER_ZERO_OR_MORE = -7;
+    private static final int PARSED_MODIFIER_ONE_OR_MORE = -8;
+
     private final String mPattern;
     private final int mType;
-    
+    private final int[] mParsedPattern;
+
+
+    private static final int MAX_PATTERN_STORAGE = 2048;
+    // workspace to use for building a parsed advanced pattern;
+    private static final int[] sParsedPatternScratch = new int[MAX_PATTERN_STORAGE];
+
     public PatternMatcher(String pattern, int type) {
         mPattern = pattern;
         mType = type;
+        if (mType == PATTERN_ADVANCED_GLOB) {
+            mParsedPattern = parseAndVerifyAdvancedPattern(pattern);
+        } else {
+            mParsedPattern = null;
+        }
     }
 
     public final String getPath() {
@@ -62,7 +112,7 @@
     }
     
     public boolean match(String str) {
-        return matchPattern(mPattern, str, mType);
+        return matchPattern(str, mPattern, mParsedPattern, mType);
     }
 
     public String toString() {
@@ -77,6 +127,9 @@
             case PATTERN_SIMPLE_GLOB:
                 type = "GLOB: ";
                 break;
+            case PATTERN_ADVANCED_GLOB:
+                type = "ADVANCED: ";
+                break;
         }
         return "PatternMatcher{" + type + mPattern + "}";
     }
@@ -88,11 +141,13 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mPattern);
         dest.writeInt(mType);
+        dest.writeIntArray(mParsedPattern);
     }
     
     public PatternMatcher(Parcel src) {
         mPattern = src.readString();
         mType = src.readInt();
+        mParsedPattern = src.createIntArray();
     }
     
     public static final Parcelable.Creator<PatternMatcher> CREATOR
@@ -106,16 +161,21 @@
         }
     };
     
-    static boolean matchPattern(String pattern, String match, int type) {
+    static boolean matchPattern(String match, String pattern, int[] parsedPattern, int type) {
         if (match == null) return false;
         if (type == PATTERN_LITERAL) {
             return pattern.equals(match);
         } if (type == PATTERN_PREFIX) {
             return match.startsWith(pattern);
-        } else if (type != PATTERN_SIMPLE_GLOB) {
-            return false;
+        } else if (type == PATTERN_SIMPLE_GLOB) {
+            return matchGlobPattern(pattern, match);
+        } else if (type == PATTERN_ADVANCED_GLOB) {
+            return matchAdvancedPattern(parsedPattern, match);
         }
-        
+        return false;
+    }
+
+    static boolean matchGlobPattern(String pattern, String match) {
         final int NP = pattern.length();
         if (NP <= 0) {
             return match.length() <= 0;
@@ -194,4 +254,310 @@
         
         return false;
     }
-}
+
+    /**
+     * Parses the advanced pattern and returns an integer array representation of it. The integer
+     * array treats each field as a character if positive and a unique token placeholder if
+     * negative. This method will throw on any pattern structure violations.
+     */
+    synchronized static int[] parseAndVerifyAdvancedPattern(String pattern) {
+        int ip = 0;
+        final int LP = pattern.length();
+
+        int it = 0;
+
+        boolean inSet = false;
+        boolean inRange = false;
+        boolean inCharClass = false;
+
+        boolean addToParsedPattern;
+
+        while (ip < LP) {
+            if (it > MAX_PATTERN_STORAGE - 3) {
+                throw new IllegalArgumentException("Pattern is too large!");
+            }
+
+            char c = pattern.charAt(ip);
+            addToParsedPattern = false;
+
+            switch (c) {
+                case '[':
+                    if (inSet) {
+                        addToParsedPattern = true; // treat as literal or char class in set
+                    } else {
+                        if (pattern.charAt(ip + 1) == '^') {
+                            sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_SET_INVERSE_START;
+                            ip++; // skip over the '^'
+                        } else {
+                            sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_SET_START;
+                        }
+                        ip++; // move to the next pattern char
+                        inSet = true;
+                        continue;
+                    }
+                    break;
+                case ']':
+                    if (!inSet) {
+                        addToParsedPattern = true; // treat as literal outside of set
+                    } else {
+                        int parsedToken = sParsedPatternScratch[it - 1];
+                        if (parsedToken == PARSED_TOKEN_CHAR_SET_START ||
+                            parsedToken == PARSED_TOKEN_CHAR_SET_INVERSE_START) {
+                            throw new IllegalArgumentException(
+                                    "You must define characters in a set.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_SET_STOP;
+                        inSet = false;
+                        inCharClass = false;
+                    }
+                    break;
+                case '{':
+                    if (!inSet) {
+                        if (it == 0 || isParsedModifier(sParsedPatternScratch[it - 1])) {
+                            throw new IllegalArgumentException("Modifier must follow a token.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_RANGE_START;
+                        ip++;
+                        inRange = true;
+                    }
+                    break;
+                case '}':
+                    if (inRange) { // only terminate the range if we're currently in one
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_RANGE_STOP;
+                        inRange = false;
+                    }
+                    break;
+                case '*':
+                    if (!inSet) {
+                        if (it == 0 || isParsedModifier(sParsedPatternScratch[it - 1])) {
+                            throw new IllegalArgumentException("Modifier must follow a token.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_ZERO_OR_MORE;
+                    }
+                    break;
+                case '+':
+                    if (!inSet) {
+                        if (it == 0 || isParsedModifier(sParsedPatternScratch[it - 1])) {
+                            throw new IllegalArgumentException("Modifier must follow a token.");
+                        }
+                        sParsedPatternScratch[it++] = PARSED_MODIFIER_ONE_OR_MORE;
+                    }
+                    break;
+                case '.':
+                    if (!inSet) {
+                        sParsedPatternScratch[it++] = PARSED_TOKEN_CHAR_ANY;
+                    }
+                    break;
+                case '\\': // escape
+                    if (ip + 1 >= LP) {
+                        throw new IllegalArgumentException("Escape found at end of pattern!");
+                    }
+                    c = pattern.charAt(++ip);
+                    addToParsedPattern = true;
+                    break;
+                default:
+                    addToParsedPattern = true;
+                    break;
+            }
+            if (inSet) {
+                if (inCharClass) {
+                    sParsedPatternScratch[it++] = c;
+                    inCharClass = false;
+                } else {
+                    // look forward for character class
+                    if (ip + 2 < LP
+                            && pattern.charAt(ip + 1) == '-'
+                            && pattern.charAt(ip + 2) != ']') {
+                        inCharClass = true;
+                        sParsedPatternScratch[it++] = c; // set first token as lower end of range
+                        ip++; // advance past dash
+                    } else { // literal
+                        sParsedPatternScratch[it++] = c; // set first token as literal
+                        sParsedPatternScratch[it++] = c; // set second set as literal
+                    }
+                }
+            } else if (inRange) {
+                int endOfSet = pattern.indexOf('}', ip);
+                if (endOfSet < 0) {
+                    throw new IllegalArgumentException("Range not ended with '}'");
+                }
+                String rangeString = pattern.substring(ip, endOfSet);
+                int commaIndex = rangeString.indexOf(',');
+                try {
+                    final int rangeMin;
+                    final int rangeMax;
+                    if (commaIndex < 0) {
+                        int parsedRange = Integer.parseInt(rangeString);
+                        rangeMin = rangeMax = parsedRange;
+                    } else {
+                        rangeMin = Integer.parseInt(rangeString.substring(0, commaIndex));
+                        if (commaIndex == rangeString.length() - 1) { // e.g. {n,} (n or more)
+                            rangeMax = Integer.MAX_VALUE;
+                        } else {
+                            rangeMax = Integer.parseInt(rangeString.substring(commaIndex + 1));
+                        }
+                    }
+                    if (rangeMin > rangeMax) {
+                        throw new IllegalArgumentException(
+                            "Range quantifier minimum is greater than maximum");
+                    }
+                    sParsedPatternScratch[it++] = rangeMin;
+                    sParsedPatternScratch[it++] = rangeMax;
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Range number format incorrect", e);
+                }
+                ip = endOfSet;
+                continue; // don't increment ip
+            } else if (addToParsedPattern) {
+                sParsedPatternScratch[it++] = c;
+            }
+            ip++;
+        }
+        if (inSet) {
+            throw new IllegalArgumentException("Set was not terminated!");
+        }
+        return Arrays.copyOf(sParsedPatternScratch, it);
+    }
+
+    private static boolean isParsedModifier(int parsedChar) {
+        return parsedChar == PARSED_MODIFIER_ONE_OR_MORE ||
+                parsedChar == PARSED_MODIFIER_ZERO_OR_MORE ||
+                parsedChar == PARSED_MODIFIER_RANGE_STOP ||
+                parsedChar == PARSED_MODIFIER_RANGE_START;
+    }
+
+    static boolean matchAdvancedPattern(int[] parsedPattern, String match) {
+
+        // create indexes
+        int ip = 0, im = 0;
+
+        // one-time length check
+        final int LP = parsedPattern.length, LM = match.length();
+
+        // The current character being analyzed in the pattern
+        int patternChar;
+
+        int tokenType;
+
+        int charSetStart = 0, charSetEnd = 0;
+
+        while (ip < LP) { // we still have content in the pattern
+
+            patternChar = parsedPattern[ip];
+            // get the match type of the next verb
+
+            switch (patternChar) {
+                case PARSED_TOKEN_CHAR_ANY:
+                    tokenType = TOKEN_TYPE_ANY;
+                    ip++;
+                    break;
+                case PARSED_TOKEN_CHAR_SET_START:
+                case PARSED_TOKEN_CHAR_SET_INVERSE_START:
+                    tokenType = patternChar == PARSED_TOKEN_CHAR_SET_START
+                            ? TOKEN_TYPE_SET
+                            : TOKEN_TYPE_INVERSE_SET;
+                    charSetStart = ip + 1; // start from the char after the set start
+                    while (++ip < LP && parsedPattern[ip] != PARSED_TOKEN_CHAR_SET_STOP);
+                    charSetEnd = ip - 1; // we're on the set stop, end is the previous
+                    ip++; // move the pointer to the next pattern entry
+                    break;
+                default:
+                    charSetStart = ip;
+                    tokenType = TOKEN_TYPE_LITERAL;
+                    ip++;
+                    break;
+            }
+
+            final int minRepetition;
+            final int maxRepetition;
+
+            // look for a match length modifier
+            if (ip >= LP) {
+                minRepetition = maxRepetition = 1;
+            } else {
+                patternChar = parsedPattern[ip];
+                switch (patternChar) {
+                    case PARSED_MODIFIER_ZERO_OR_MORE:
+                        minRepetition = 0;
+                        maxRepetition = Integer.MAX_VALUE;
+                        ip++;
+                        break;
+                    case PARSED_MODIFIER_ONE_OR_MORE:
+                        minRepetition = 1;
+                        maxRepetition = Integer.MAX_VALUE;
+                        ip++;
+                        break;
+                    case PARSED_MODIFIER_RANGE_START:
+                        minRepetition = parsedPattern[++ip];
+                        maxRepetition = parsedPattern[++ip];
+                        ip += 2; // step over PARSED_MODIFIER_RANGE_STOP and on to the next token
+                        break;
+                    default:
+                        minRepetition = maxRepetition = 1; // implied literal
+                        break;
+                }
+            }
+            if (minRepetition > maxRepetition) {
+                return false;
+            }
+
+            // attempt to match as many characters as possible
+            int matched = matchChars(match, im, LM, tokenType, minRepetition, maxRepetition,
+                    parsedPattern, charSetStart, charSetEnd);
+
+            // if we found a conflict, return false immediately
+            if (matched == NO_MATCH) {
+                return false;
+            }
+
+            // move the match pointer the number of characters matched
+            im += matched;
+        }
+        return ip >= LP && im >= LM; // have parsed entire string and regex
+    }
+
+    private static int matchChars(String match, int im, final int lm, int tokenType,
+            int minRepetition, int maxRepetition, int[] parsedPattern,
+            int tokenStart, int tokenEnd) {
+        int matched = 0;
+
+        while(matched < maxRepetition
+                && matchChar(match, im + matched, lm, tokenType, parsedPattern, tokenStart,
+                    tokenEnd)) {
+            matched++;
+        }
+
+        return matched < minRepetition ? NO_MATCH : matched;
+    }
+
+    private static boolean matchChar(String match, int im, final int lm, int tokenType,
+            int[] parsedPattern, int tokenStart, int tokenEnd) {
+        if (im >= lm) { // we've overrun the string, no match
+            return false;
+        }
+        switch (tokenType) {
+            case TOKEN_TYPE_ANY:
+                return true;
+            case TOKEN_TYPE_SET:
+                for (int i = tokenStart; i < tokenEnd; i += 2) {
+                    char matchChar = match.charAt(im);
+                    if (matchChar >= parsedPattern[i] && matchChar <= parsedPattern[i + 1]) {
+                        return true;
+                    }
+                }
+                return false;
+            case TOKEN_TYPE_INVERSE_SET:
+                for (int i = tokenStart; i < tokenEnd; i += 2) {
+                    char matchChar = match.charAt(im);
+                    if (matchChar >= parsedPattern[i] && matchChar <= parsedPattern[i + 1]) {
+                        return false;
+                    }
+                }
+                return true;
+            case TOKEN_TYPE_LITERAL:
+                return match.charAt(im) == parsedPattern[tokenStart];
+            default:
+                return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 507379b..90bd11f 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -724,6 +724,7 @@
             String line = null;
             int bytesWrittenInMiB = -1, bytesStashedInMiB = -1;
             int timeTotal = -1;
+            int uncryptTime = -1;
             int sourceVersion = -1;
             while ((line = in.readLine()) != null) {
                 // Here is an example of lines in last_install:
@@ -759,6 +760,8 @@
 
                 if (line.startsWith("time")) {
                     timeTotal = scaled;
+                } else if (line.startsWith("uncrypt_time")) {
+                    uncryptTime = scaled;
                 } else if (line.startsWith("source_build")) {
                     sourceVersion = scaled;
                 } else if (line.startsWith("bytes_written")) {
@@ -774,6 +777,9 @@
             if (timeTotal != -1) {
                 MetricsLogger.histogram(context, "ota_time_total", timeTotal);
             }
+            if (uncryptTime != -1) {
+                MetricsLogger.histogram(context, "ota_uncrypt_time", uncryptTime);
+            }
             if (sourceVersion != -1) {
                 MetricsLogger.histogram(context, "ota_source_version", sourceVersion);
             }
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 5849350..3546e17 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -288,20 +288,22 @@
      * @see #beginBroadcast
      */
     public void finishBroadcast() {
-        if (mBroadcastCount < 0) {
-            throw new IllegalStateException(
-                    "finishBroadcast() called outside of a broadcast");
-        }
-        
-        Object[] active = mActiveBroadcast;
-        if (active != null) {
-            final int N = mBroadcastCount;
-            for (int i=0; i<N; i++) {
-                active[i] = null;
+        synchronized (mCallbacks) {
+            if (mBroadcastCount < 0) {
+                throw new IllegalStateException(
+                        "finishBroadcast() called outside of a broadcast");
             }
+
+            Object[] active = mActiveBroadcast;
+            if (active != null) {
+                final int N = mBroadcastCount;
+                for (int i=0; i<N; i++) {
+                    active[i] = null;
+                }
+            }
+
+            mBroadcastCount = -1;
         }
-        
-        mBroadcastCount = -1;
     }
 
     /**
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index ec1e102..2bfe33b 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -58,14 +58,14 @@
  * This class contains a {@code key} that will be used as the key into the
  * {@link SharedPreferences}. It is up to the subclass to decide how to store
  * the value.
- * 
+ *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For information about building a settings UI with Preferences,
  * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
  * guide.</p>
  * </div>
- * 
+ *
  * @attr ref android.R.styleable#Preference_icon
  * @attr ref android.R.styleable#Preference_key
  * @attr ref android.R.styleable#Preference_title
@@ -89,13 +89,13 @@
 
     private Context mContext;
     private PreferenceManager mPreferenceManager;
-    
+
     /**
      * Set when added to hierarchy since we need a unique ID within that
      * hierarchy.
      */
     private long mId;
-    
+
     private OnPreferenceChangeListener mOnChangeListener;
     private OnPreferenceClickListener mOnClickListener;
 
@@ -120,22 +120,22 @@
     private Object mDefaultValue;
     private boolean mDependencyMet = true;
     private boolean mParentDependencyMet = true;
-    
+
     /**
      * @see #setShouldDisableView(boolean)
      */
     private boolean mShouldDisableView = true;
-    
+
     private int mLayoutResId = com.android.internal.R.layout.preference;
     private int mWidgetLayoutResId;
     private boolean mCanRecycleLayout = true;
-    
+
     private OnPreferenceChangeInternalListener mListener;
-    
+
     private List<Preference> mDependents;
-    
+
     private boolean mBaseMethodCalled;
-    
+
     /**
      * Interface definition for a callback to be invoked when the value of this
      * {@link Preference} has been changed by the user and is
@@ -147,7 +147,7 @@
          * Called when a Preference has been changed by the user. This is
          * called before the state of the Preference is about to be updated and
          * before the state is persisted.
-         * 
+         *
          * @param preference The changed Preference.
          * @param newValue The new value of the Preference.
          * @return True to update the state of the Preference with the new value.
@@ -177,14 +177,14 @@
     interface OnPreferenceChangeInternalListener {
         /**
          * Called when this Preference has changed.
-         * 
+         *
          * @param preference This preference.
          */
         void onPreferenceChange(Preference preference);
-        
+
         /**
          * Called when this group has added/removed {@link Preference}(s).
-         * 
+         *
          * @param preference This Preference.
          */
         void onPreferenceHierarchyChange(Preference preference);
@@ -220,7 +220,7 @@
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes);
         for (int i = a.getIndexCount() - 1; i >= 0; i--) {
-            int attr = a.getIndex(i); 
+            int attr = a.getIndex(i);
             switch (attr) {
                 case com.android.internal.R.styleable.Preference_icon:
                     mIconResId = a.getResourceId(attr, 0);
@@ -229,16 +229,16 @@
                 case com.android.internal.R.styleable.Preference_key:
                     mKey = a.getString(attr);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_title:
                     mTitleRes = a.getResourceId(attr, 0);
-                    mTitle = a.getString(attr);
+                    mTitle = a.getText(attr);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_summary:
-                    mSummary = a.getString(attr);
+                    mSummary = a.getText(attr);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_order:
                     mOrder = a.getInt(attr, mOrder);
                     break;
@@ -254,27 +254,27 @@
                 case com.android.internal.R.styleable.Preference_widgetLayout:
                     mWidgetLayoutResId = a.getResourceId(attr, mWidgetLayoutResId);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_enabled:
                     mEnabled = a.getBoolean(attr, true);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_selectable:
                     mSelectable = a.getBoolean(attr, true);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_persistent:
                     mPersistent = a.getBoolean(attr, mPersistent);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_dependency:
                     mDependencyKey = a.getString(attr);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_defaultValue:
                     mDefaultValue = onGetDefaultValue(a, attr);
                     break;
-                    
+
                 case com.android.internal.R.styleable.Preference_shouldDisableView:
                     mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
                     break;
@@ -312,14 +312,14 @@
     public Preference(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
-    
+
     /**
      * Constructor that is called when inflating a Preference from XML. This is
      * called when a Preference is being constructed from an XML file, supplying
      * attributes that were specified in the XML file. This version uses a
      * default style of 0, so the only attribute values applied are those in the
      * Context's Theme and the given AttributeSet.
-     * 
+     *
      * @param context The Context this is associated with, through which it can
      *            access the current theme, resources, {@link SharedPreferences},
      *            etc.
@@ -333,7 +333,7 @@
 
     /**
      * Constructor to create a Preference.
-     * 
+     *
      * @param context The Context in which to store Preference values.
      */
     public Preference(Context context) {
@@ -348,7 +348,7 @@
      * <p>
      * For example, if the value type is String, the body of the method would
      * proxy to {@link TypedArray#getString(int)}.
-     * 
+     *
      * @param a The set of attributes.
      * @param index The index of the default value attribute.
      * @return The default value of this preference type.
@@ -356,20 +356,20 @@
     protected Object onGetDefaultValue(TypedArray a, int index) {
         return null;
     }
-    
+
     /**
      * Sets an {@link Intent} to be used for
      * {@link Context#startActivity(Intent)} when this Preference is clicked.
-     * 
+     *
      * @param intent The intent associated with this Preference.
      */
     public void setIntent(Intent intent) {
         mIntent = intent;
     }
-    
+
     /**
      * Return the {@link Intent} associated with this Preference.
-     * 
+     *
      * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML. 
      */
     public Intent getIntent() {
@@ -423,7 +423,7 @@
      * {@link android.R.id#widget_frame} to be the parent of the specific widget
      * for this Preference. It should similarly contain
      * {@link android.R.id#title} and {@link android.R.id#summary}.
-     * 
+     *
      * @param layoutResId The layout resource ID to be inflated and returned as
      *            a {@link View}.
      * @see #setWidgetLayoutResource(int)
@@ -436,23 +436,23 @@
 
         mLayoutResId = layoutResId;
     }
-    
+
     /**
      * Gets the layout resource that will be shown as the {@link View} for this Preference.
-     * 
+     *
      * @return The layout resource ID.
      */
     @LayoutRes
     public int getLayoutResource() {
         return mLayoutResId;
     }
-    
+
     /**
      * Sets the layout for the controllable widget portion of this Preference. This
      * is inflated into the main layout. For example, a {@link CheckBoxPreference}
      * would specify a custom layout (consisting of just the CheckBox) here,
      * instead of creating its own main layout.
-     * 
+     *
      * @param widgetLayoutResId The layout resource ID to be inflated into the
      *            main layout.
      * @see #setLayoutResource(int)
@@ -467,17 +467,17 @@
 
     /**
      * Gets the layout resource for the controllable widget portion of this Preference.
-     * 
+     *
      * @return The layout resource ID.
      */
     @LayoutRes
     public int getWidgetLayoutResource() {
         return mWidgetLayoutResId;
     }
-    
+
     /**
      * Gets the View that will be shown in the {@link PreferenceActivity}.
-     * 
+     *
      * @param convertView The old View to reuse, if possible. Note: You should
      *            check that this View is non-null and of an appropriate type
      *            before using. If it is not possible to convert this View to
@@ -495,7 +495,7 @@
         onBindView(convertView);
         return convertView;
     }
-    
+
     /**
      * Creates the View to be shown for this Preference in the
      * {@link PreferenceActivity}. The default behavior is to inflate the main
@@ -504,7 +504,7 @@
      * {@link android.R.id#widget_frame}.
      * <p>
      * Make sure to call through to the superclass's implementation.
-     * 
+     *
      * @param parent The parent that this View will eventually be attached to.
      * @return The View that displays this Preference.
      * @see #onBindView(View)
@@ -512,10 +512,10 @@
     @CallSuper
     protected View onCreateView(ViewGroup parent) {
         final LayoutInflater layoutInflater =
-            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        
-        final View layout = layoutInflater.inflate(mLayoutResId, parent, false); 
-        
+                (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        final View layout = layoutInflater.inflate(mLayoutResId, parent, false);
+
         final ViewGroup widgetFrame = (ViewGroup) layout
                 .findViewById(com.android.internal.R.id.widget_frame);
         if (widgetFrame != null) {
@@ -527,7 +527,7 @@
         }
         return layout;
     }
-    
+
     /**
      * Binds the created View to the data for this Preference.
      * <p>
@@ -535,7 +535,7 @@
      * set properties on them.
      * <p>
      * Make sure to call through to the superclass's implementation.
-     * 
+     *
      * @param view The View that shows this Preference.
      * @see #onCreateView(ViewGroup)
      */
@@ -592,7 +592,7 @@
      */
     private void setEnabledStateOnViews(View v, boolean enabled) {
         v.setEnabled(enabled);
-        
+
         if (v instanceof ViewGroup) {
             final ViewGroup vg = (ViewGroup) v;
             for (int i = vg.getChildCount() - 1; i >= 0; i--) {
@@ -600,14 +600,14 @@
             }
         }
     }
-    
+
     /**
      * Sets the order of this Preference with respect to other
      * Preference objects on the same level. If this is not specified, the
      * default behavior is to sort alphabetically. The
      * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
      * Preference objects based on the order they appear in the XML.
-     * 
+     *
      * @param order The order for this Preference. A lower value will be shown
      *            first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
      *            allow ordering from XML.
@@ -622,11 +622,11 @@
             notifyHierarchyChanged();
         }
     }
-    
+
     /**
      * Gets the order of this Preference with respect to other Preference objects
      * on the same level.
-     * 
+     *
      * @return The order of this Preference.
      * @see #setOrder(int)
      */
@@ -639,7 +639,7 @@
      * This title will be placed into the ID
      * {@link android.R.id#title} within the View created by
      * {@link #onCreateView(ViewGroup)}.
-     * 
+     *
      * @param title The title for this Preference.
      */
     public void setTitle(CharSequence title) {
@@ -649,10 +649,10 @@
             notifyChanged();
         }
     }
-    
+
     /**
      * Sets the title for this Preference with a resource ID. 
-     * 
+     *
      * @see #setTitle(CharSequence)
      * @param titleResId The title as a resource ID.
      */
@@ -660,7 +660,7 @@
         setTitle(mContext.getString(titleResId));
         mTitleRes = titleResId;
     }
-    
+
     /**
      * Returns the title resource ID of this Preference.  If the title did
      * not come from a resource, 0 is returned.
@@ -675,7 +675,7 @@
 
     /**
      * Returns the title of this Preference.
-     * 
+     *
      * @return The title.
      * @see #setTitle(CharSequence)
      */
@@ -688,7 +688,7 @@
      * This icon will be placed into the ID
      * {@link android.R.id#icon} within the View created by
      * {@link #onCreateView(ViewGroup)}.
-     * 
+     *
      * @param icon The optional icon for this Preference.
      */
     public void setIcon(Drawable icon) {
@@ -701,7 +701,7 @@
 
     /**
      * Sets the icon for this Preference with a resource ID. 
-     * 
+     *
      * @see #setIcon(Drawable)
      * @param iconResId The icon as a resource ID.
      */
@@ -714,7 +714,7 @@
 
     /**
      * Returns the icon of this Preference.
-     * 
+     *
      * @return The icon.
      * @see #setIcon(Drawable)
      */
@@ -727,7 +727,7 @@
 
     /**
      * Returns the summary of this Preference.
-     * 
+     *
      * @return The summary.
      * @see #setSummary(CharSequence)
      */
@@ -737,7 +737,7 @@
 
     /**
      * Sets the summary for this Preference with a CharSequence. 
-     * 
+     *
      * @param summary The summary for the preference.
      */
     public void setSummary(CharSequence summary) {
@@ -749,18 +749,18 @@
 
     /**
      * Sets the summary for this Preference with a resource ID. 
-     * 
+     *
      * @see #setSummary(CharSequence)
      * @param summaryResId The summary as a resource.
      */
     public void setSummary(@StringRes int summaryResId) {
         setSummary(mContext.getString(summaryResId));
     }
-    
+
     /**
      * Sets whether this Preference is enabled. If disabled, it will
      * not handle clicks.
-     * 
+     *
      * @param enabled Set true to enable it.
      */
     public void setEnabled(boolean enabled) {
@@ -773,10 +773,10 @@
             notifyChanged();
         }
     }
-    
+
     /**
      * Checks whether this Preference should be enabled in the list.
-     * 
+     *
      * @return True if this Preference is enabled, false otherwise.
      */
     public boolean isEnabled() {
@@ -785,7 +785,7 @@
 
     /**
      * Sets whether this Preference is selectable.
-     * 
+     *
      * @param selectable Set true to make it selectable.
      */
     public void setSelectable(boolean selectable) {
@@ -794,10 +794,10 @@
             notifyChanged();
         }
     }
-    
+
     /**
      * Checks whether this Preference should be selectable in the list.
-     * 
+     *
      * @return True if it is selectable, false otherwise.
      */
     public boolean isSelectable() {
@@ -811,7 +811,7 @@
      * For example, set this and {@link #setEnabled(boolean)} to false for
      * preferences that are only displaying information and 1) should not be
      * clickable 2) should not have the view set to the disabled state.
-     * 
+     *
      * @param shouldDisableView Set true if this preference should disable its view
      *            when the preference is disabled.
      */
@@ -819,7 +819,7 @@
         mShouldDisableView = shouldDisableView;
         notifyChanged();
     }
-    
+
     /**
      * Checks whether this Preference should disable its view when it's action is disabled.
      * @see #setShouldDisableView(boolean)
@@ -832,13 +832,13 @@
     /**
      * Returns a unique ID for this Preference.  This ID should be unique across all
      * Preference objects in a hierarchy.
-     * 
+     *
      * @return A unique ID for this Preference.
      */
     long getId() {
         return mId;
     }
-    
+
     /**
      * Processes a click on the preference. This includes saving the value to
      * the {@link SharedPreferences}. However, the overridden method should
@@ -847,93 +847,93 @@
      */
     protected void onClick() {
     }
-    
+
     /**
      * Sets the key for this Preference, which is used as a key to the
      * {@link SharedPreferences}. This should be unique for the package.
-     * 
+     *
      * @param key The key for the preference.
      */
     public void setKey(String key) {
         mKey = key;
-        
+
         if (mRequiresKey && !hasKey()) {
             requireKey();
         }
     }
-    
+
     /**
      * Gets the key for this Preference, which is also the key used for storing
      * values into SharedPreferences.
-     * 
+     *
      * @return The key.
      */
     public String getKey() {
         return mKey;
     }
-    
+
     /**
      * Checks whether the key is present, and if it isn't throws an
      * exception. This should be called by subclasses that store preferences in
      * the {@link SharedPreferences}.
-     * 
+     *
      * @throws IllegalStateException If there is no key assigned.
      */
     void requireKey() {
         if (mKey == null) {
             throw new IllegalStateException("Preference does not have a key assigned.");
         }
-        
+
         mRequiresKey = true;
     }
-    
+
     /**
      * Checks whether this Preference has a valid key.
-     * 
+     *
      * @return True if the key exists and is not a blank string, false otherwise.
      */
     public boolean hasKey() {
         return !TextUtils.isEmpty(mKey);
     }
-    
+
     /**
      * Checks whether this Preference is persistent. If it is, it stores its value(s) into
      * the persistent {@link SharedPreferences} storage.
-     * 
+     *
      * @return True if it is persistent.
      */
     public boolean isPersistent() {
         return mPersistent;
     }
-    
+
     /**
      * Checks whether, at the given time this method is called,
      * this Preference should store/restore its value(s) into the
      * {@link SharedPreferences}. This, at minimum, checks whether this
      * Preference is persistent and it currently has a key. Before you
      * save/restore from the {@link SharedPreferences}, check this first.
-     * 
+     *
      * @return True if it should persist the value.
      */
     protected boolean shouldPersist() {
         return mPreferenceManager != null && isPersistent() && hasKey();
     }
-    
+
     /**
      * Sets whether this Preference is persistent. When persistent,
      * it stores its value(s) into the persistent {@link SharedPreferences}
      * storage.
-     * 
+     *
      * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}.
      */
     public void setPersistent(boolean persistent) {
         mPersistent = persistent;
     }
-    
+
     /**
      * Call this method after the user changes the preference, but before the
      * internal state is set. This allows the client to ignore the user value.
-     * 
+     *
      * @param newValue The new value of this Preference.
      * @return True if the user value should be set as the preference
      *         value (and persisted).
@@ -941,11 +941,11 @@
     protected boolean callChangeListener(Object newValue) {
         return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue);
     }
-    
+
     /**
      * Sets the callback to be invoked when this Preference is changed by the
      * user (but before the internal state has been updated).
-     * 
+     *
      * @param onPreferenceChangeListener The callback to be invoked.
      */
     public void setOnPreferenceChangeListener(OnPreferenceChangeListener onPreferenceChangeListener) {
@@ -955,7 +955,7 @@
     /**
      * Returns the callback to be invoked when this Preference is changed by the
      * user (but before the internal state has been updated).
-     * 
+     *
      * @return The callback to be invoked.
      */
     public OnPreferenceChangeListener getOnPreferenceChangeListener() {
@@ -964,7 +964,7 @@
 
     /**
      * Sets the callback to be invoked when this Preference is clicked.
-     * 
+     *
      * @param onPreferenceClickListener The callback to be invoked.
      */
     public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
@@ -973,7 +973,7 @@
 
     /**
      * Returns the callback to be invoked when this Preference is clicked.
-     * 
+     *
      * @return The callback to be invoked.
      */
     public OnPreferenceClickListener getOnPreferenceClickListener() {
@@ -982,24 +982,24 @@
 
     /**
      * Called when a click should be performed.
-     * 
+     *
      * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
      *            listener should be called in the proper order (between other
      *            processing). May be null.
      * @hide
      */
     public void performClick(PreferenceScreen preferenceScreen) {
-        
+
         if (!isEnabled()) {
             return;
         }
-        
+
         onClick();
-        
+
         if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
             return;
         }
-        
+
         PreferenceManager preferenceManager = getPreferenceManager();
         if (preferenceManager != null) {
             PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
@@ -1009,7 +1009,7 @@
                 return;
             }
         }
-        
+
         if (mIntent != null) {
             Context context = getContext();
             context.startActivity(mIntent);
@@ -1026,19 +1026,19 @@
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         return false;
     }
-    
+
     /**
      * Returns the {@link android.content.Context} of this Preference. 
      * Each Preference in a Preference hierarchy can be
      * from different Context (for example, if multiple activities provide preferences into a single
      * {@link PreferenceActivity}). This Context will be used to save the Preference values.
-     * 
+     *
      * @return The Context of this Preference.
      */
     public Context getContext() {
         return mContext;
     }
-    
+
     /**
      * Returns the {@link SharedPreferences} where this Preference can read its
      * value(s). Usually, it's easier to use one of the helper read methods:
@@ -1051,7 +1051,7 @@
      * right away and hence not show up in the returned
      * {@link SharedPreferences}, this is intended behavior to improve
      * performance.
-     * 
+     *
      * @return The {@link SharedPreferences} where this Preference reads its
      *         value(s), or null if it isn't attached to a Preference hierarchy.
      * @see #getEditor()
@@ -1060,10 +1060,10 @@
         if (mPreferenceManager == null) {
             return null;
         }
-        
+
         return mPreferenceManager.getSharedPreferences();
     }
-    
+
     /**
      * Returns an {@link SharedPreferences.Editor} where this Preference can
      * save its value(s). Usually it's easier to use one of the helper save
@@ -1076,7 +1076,7 @@
      * In some cases, writes to this will not be committed right away and hence
      * not show up in the SharedPreferences, this is intended behavior to
      * improve performance.
-     * 
+     *
      * @return A {@link SharedPreferences.Editor} where this preference saves
      *         its value(s), or null if it isn't attached to a Preference
      *         hierarchy.
@@ -1087,15 +1087,15 @@
         if (mPreferenceManager == null) {
             return null;
         }
-        
+
         return mPreferenceManager.getEditor();
     }
-    
+
     /**
      * Returns whether the {@link Preference} should commit its saved value(s) in
      * {@link #getEditor()}. This may return false in situations where batch
      * committing is being done (by the manager) to improve performance.
-     * 
+     *
      * @return Whether the Preference should commit its saved value(s).
      * @see #getEditor()
      */
@@ -1103,13 +1103,13 @@
         if (mPreferenceManager == null) {
             return false;
         }
-        
+
         return mPreferenceManager.shouldCommit();
     }
-    
+
     /**
      * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
-     * 
+     *
      * @param another The Preference to compare to this one.
      * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
      *          greater than 0 if this Preference sorts after <var>another</var>.
@@ -1131,10 +1131,10 @@
             return CharSequences.compareToIgnoreCase(mTitle, another.mTitle);
         }
     }
-    
+
     /**
      * Sets the internal change listener.
-     * 
+     *
      * @param listener The listener.
      * @see #notifyChanged()
      */
@@ -1150,7 +1150,7 @@
             mListener.onPreferenceChange(this);
         }
     }
-    
+
     /**
      * Should be called when a Preference has been
      * added/removed from this group, or the ordering should be
@@ -1164,27 +1164,27 @@
 
     /**
      * Gets the {@link PreferenceManager} that manages this Preference object's tree.
-     * 
+     *
      * @return The {@link PreferenceManager}.
      */
     public PreferenceManager getPreferenceManager() {
         return mPreferenceManager;
     }
-    
+
     /**
      * Called when this Preference has been attached to a Preference hierarchy.
      * Make sure to call the super implementation.
-     * 
+     *
      * @param preferenceManager The PreferenceManager of the hierarchy.
      */
     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
         mPreferenceManager = preferenceManager;
-        
+
         mId = preferenceManager.getNextId();
-        
+
         dispatchSetInitialValue();
     }
-    
+
     /**
      * Called when the Preference hierarchy has been attached to the
      * {@link PreferenceActivity}. This can also be called when this
@@ -1198,9 +1198,9 @@
     }
 
     private void registerDependency() {
-        
+
         if (TextUtils.isEmpty(mDependencyKey)) return;
-        
+
         Preference preference = findPreferenceInHierarchy(mDependencyKey);
         if (preference != null) {
             preference.registerDependent(this);
@@ -1218,14 +1218,14 @@
             }
         }
     }
-    
+
     /**
      * Finds a Preference in this hierarchy (the whole thing,
      * even above/below your {@link PreferenceScreen} screen break) with the given
      * key.
      * <p>
      * This only functions after we have been attached to a hierarchy.
-     * 
+     *
      * @param key The key of the Preference to find.
      * @return The Preference that uses the given key.
      */
@@ -1233,16 +1233,16 @@
         if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
             return null;
         }
-        
+
         return mPreferenceManager.findPreference(key);
     }
-    
+
     /**
      * Adds a dependent Preference on this Preference so we can notify it.
      * Usually, the dependent Preference registers itself (it's good for it to
      * know it depends on something), so please use
      * {@link Preference#setDependency(String)} on the dependent Preference.
-     * 
+     *
      * @param dependent The dependent Preference that will be enabled/disabled
      *            according to the state of this Preference.
      */
@@ -1250,15 +1250,15 @@
         if (mDependents == null) {
             mDependents = new ArrayList<Preference>();
         }
-        
+
         mDependents.add(dependent);
-        
+
         dependent.onDependencyChanged(this, shouldDisableDependents());
     }
-    
+
     /**
      * Removes a dependent Preference on this Preference.
-     * 
+     *
      * @param dependent The dependent Preference that will be enabled/disabled
      *            according to the state of this Preference.
      * @return Returns the same Preference object, for chaining multiple calls
@@ -1269,21 +1269,21 @@
             mDependents.remove(dependent);
         }
     }
-    
+
     /**
      * Notifies any listening dependents of a change that affects the
      * dependency.
-     * 
+     *
      * @param disableDependents Whether this Preference should disable
      *            its dependents.
      */
     public void notifyDependencyChange(boolean disableDependents) {
         final List<Preference> dependents = mDependents;
-        
+
         if (dependents == null) {
             return;
         }
-        
+
         final int dependentsCount = dependents.size();
         for (int i = 0; i < dependentsCount; i++) {
             dependents.get(i).onDependencyChanged(this, disableDependents);
@@ -1292,7 +1292,7 @@
 
     /**
      * Called when the dependency changes.
-     * 
+     *
      * @param dependency The Preference that this Preference depends on.
      * @param disableDependent Set true to disable this Preference.
      */
@@ -1327,38 +1327,38 @@
     /**
      * Checks whether this preference's dependents should currently be
      * disabled.
-     * 
+     *
      * @return True if the dependents should be disabled, otherwise false.
      */
     public boolean shouldDisableDependents() {
         return !isEnabled();
     }
-    
+
     /**
      * Sets the key of a Preference that this Preference will depend on. If that
      * Preference is not set or is off, this Preference will be disabled.
-     * 
+     *
      * @param dependencyKey The key of the Preference that this depends on.
      */
     public void setDependency(String dependencyKey) {
         // Unregister the old dependency, if we had one
         unregisterDependency();
-        
+
         // Register the new
         mDependencyKey = dependencyKey;
         registerDependency();
     }
-    
+
     /**
      * Returns the key of the dependency on this Preference.
-     * 
+     *
      * @return The key of the dependency.
      * @see #setDependency(String)
      */
     public String getDependency() {
         return mDependencyKey;
     }
-    
+
     /**
      * Called when this Preference is being removed from the hierarchy. You
      * should remove any references to this Preference that you know about. Make
@@ -1368,18 +1368,18 @@
     protected void onPrepareForRemoval() {
         unregisterDependency();
     }
-    
+
     /**
      * Sets the default value for this Preference, which will be set either if
      * persistence is off or persistence is on and the preference is not found
      * in the persistent storage.
-     * 
+     *
      * @param defaultValue The default value.
      */
     public void setDefaultValue(Object defaultValue) {
         mDefaultValue = defaultValue;
     }
-    
+
     private void dispatchSetInitialValue() {
         // By now, we know if we are persistent.
         final boolean shouldPersist = shouldPersist();
@@ -1391,7 +1391,7 @@
             onSetInitialValue(true, null);
         }
     }
-    
+
     /**
      * Implement this to set the initial value of the Preference. 
      * <p>
@@ -1403,7 +1403,7 @@
      * <p>
      * This may not always be called. One example is if it should not persist
      * but there is no default value given.
-     * 
+     *
      * @param restorePersistedValue True to restore the persisted value;
      *            false to use the given <var>defaultValue</var>.
      * @param defaultValue The default value for this Preference. Only use this
@@ -1424,14 +1424,14 @@
             }
         }
     }
-    
+
     /**
      * Attempts to persist a String to the {@link android.content.SharedPreferences}.
      * <p>
      * This will check if this Preference is persistent, get an editor from
      * the {@link PreferenceManager}, put in the string, and check if we should commit (and
      * commit if so).
-     * 
+     *
      * @param value The value to persist.
      * @return True if the Preference is persistent. (This is not whether the
      *         value was persisted, since we may not necessarily commit if there
@@ -1445,7 +1445,7 @@
                 // It's already there, so the same as persisting
                 return true;
             }
-            
+
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putString(mKey, value);
             tryCommit(editor);
@@ -1453,13 +1453,13 @@
         }
         return false;
     }
-    
+
     /**
      * Attempts to get a persisted String from the {@link android.content.SharedPreferences}.
      * <p>
      * This will check if this Preference is persistent, get the SharedPreferences
      * from the {@link PreferenceManager}, and get the value.
-     * 
+     *
      * @param defaultReturnValue The default value to return if either the
      *            Preference is not persistent or the Preference is not in the
      *            shared preferences.
@@ -1471,17 +1471,17 @@
         if (!shouldPersist()) {
             return defaultReturnValue;
         }
-        
+
         return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
     }
-    
+
     /**
      * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}.
      * <p>
      * This will check if this Preference is persistent, get an editor from
      * the {@link PreferenceManager}, put in the strings, and check if we should commit (and
      * commit if so).
-     * 
+     *
      * @param values The values to persist.
      * @return True if the Preference is persistent. (This is not whether the
      *         value was persisted, since we may not necessarily commit if there
@@ -1495,7 +1495,7 @@
                 // It's already there, so the same as persisting
                 return true;
             }
-            
+
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putStringSet(mKey, values);
             tryCommit(editor);
@@ -1510,7 +1510,7 @@
      * <p>
      * This will check if this Preference is persistent, get the SharedPreferences
      * from the {@link PreferenceManager}, and get the value.
-     * 
+     *
      * @param defaultReturnValue The default value to return if either the
      *            Preference is not persistent or the Preference is not in the
      *            shared preferences.
@@ -1522,13 +1522,13 @@
         if (!shouldPersist()) {
             return defaultReturnValue;
         }
-        
+
         return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
     }
-    
+
     /**
      * Attempts to persist an int to the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param value The value to persist.
      * @return True if the Preference is persistent. (This is not whether the
      *         value was persisted, since we may not necessarily commit if there
@@ -1542,7 +1542,7 @@
                 // It's already there, so the same as persisting
                 return true;
             }
-            
+
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putInt(mKey, value);
             tryCommit(editor);
@@ -1550,10 +1550,10 @@
         }
         return false;
     }
-    
+
     /**
      * Attempts to get a persisted int from the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
@@ -1566,13 +1566,13 @@
         if (!shouldPersist()) {
             return defaultReturnValue;
         }
-        
+
         return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
     }
-    
+
     /**
      * Attempts to persist a float to the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param value The value to persist.
      * @return True if this Preference is persistent. (This is not whether the
      *         value was persisted, since we may not necessarily commit if there
@@ -1594,10 +1594,10 @@
         }
         return false;
     }
-    
+
     /**
      * Attempts to get a persisted float from the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
@@ -1610,13 +1610,13 @@
         if (!shouldPersist()) {
             return defaultReturnValue;
         }
-        
+
         return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
     }
-    
+
     /**
      * Attempts to persist a long to the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param value The value to persist.
      * @return True if this Preference is persistent. (This is not whether the
      *         value was persisted, since we may not necessarily commit if there
@@ -1630,7 +1630,7 @@
                 // It's already there, so the same as persisting
                 return true;
             }
-            
+
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putLong(mKey, value);
             tryCommit(editor);
@@ -1638,10 +1638,10 @@
         }
         return false;
     }
-    
+
     /**
      * Attempts to get a persisted long from the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
@@ -1654,13 +1654,13 @@
         if (!shouldPersist()) {
             return defaultReturnValue;
         }
-        
+
         return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
     }
-    
+
     /**
      * Attempts to persist a boolean to the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param value The value to persist.
      * @return True if this Preference is persistent. (This is not whether the
      *         value was persisted, since we may not necessarily commit if there
@@ -1674,7 +1674,7 @@
                 // It's already there, so the same as persisting
                 return true;
             }
-            
+
             SharedPreferences.Editor editor = mPreferenceManager.getEditor();
             editor.putBoolean(mKey, value);
             tryCommit(editor);
@@ -1682,10 +1682,10 @@
         }
         return false;
     }
-    
+
     /**
      * Attempts to get a persisted boolean from the {@link android.content.SharedPreferences}.
-     * 
+     *
      * @param defaultReturnValue The default value to return if either this
      *            Preference is not persistent or this Preference is not in the
      *            SharedPreferences.
@@ -1698,26 +1698,26 @@
         if (!shouldPersist()) {
             return defaultReturnValue;
         }
-        
+
         return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
     }
-    
+
     boolean canRecycleLayout() {
         return mCanRecycleLayout;
     }
-    
+
     @Override
     public String toString() {
         return getFilterableStringBuilder().toString();
     }
-        
+
     /**
      * Returns the text that will be used to filter this Preference depending on
      * user input.
      * <p>
      * If overridding and calling through to the superclass, make sure to prepend
      * your additions with a space.
-     * 
+     *
      * @return Text as a {@link StringBuilder} that will be used to filter this
      *         preference. By default, this is the title and summary
      *         (concatenated with a space).
@@ -1741,9 +1741,9 @@
 
     /**
      * Store this Preference hierarchy's frozen state into the given container.
-     * 
+     *
      * @param container The Bundle in which to save the instance of this Preference.
-     * 
+     *
      * @see #restoreHierarchyState
      * @see #onSaveInstanceState
      */
@@ -1755,9 +1755,9 @@
      * Called by {@link #saveHierarchyState} to store the instance for this Preference and its children.
      * May be overridden to modify how the save happens for children. For example, some
      * Preference objects may want to not store an instance for their children.
-     * 
+     *
      * @param container The Bundle in which to save the instance of this Preference.
-     * 
+     *
      * @see #saveHierarchyState
      * @see #onSaveInstanceState
      */
@@ -1780,7 +1780,7 @@
      * state that can later be used to create a new instance with that same
      * state. This state should only contain information that is not persistent
      * or can be reconstructed later.
-     * 
+     *
      * @return A Parcelable object containing the current dynamic state of
      *         this Preference, or null if there is nothing interesting to save.
      *         The default implementation returns null.
@@ -1794,9 +1794,9 @@
 
     /**
      * Restore this Preference hierarchy's previously saved state from the given container.
-     * 
+     *
      * @param container The Bundle that holds the previously saved state.
-     * 
+     *
      * @see #saveHierarchyState
      * @see #onRestoreInstanceState
      */
@@ -1809,7 +1809,7 @@
      * Preference and its children. May be overridden to modify how restoring
      * happens to the children of a Preference. For example, some Preference objects may
      * not want to save state for their children.
-     * 
+     *
      * @param container The Bundle that holds the previously saved state.
      * @see #restoreHierarchyState
      * @see #onRestoreInstanceState
@@ -1832,7 +1832,7 @@
      * Hook allowing a Preference to re-apply a representation of its internal
      * state that had previously been generated by {@link #onSaveInstanceState}.
      * This function will never be called with a null state.
-     * 
+     *
      * @param state The saved state that had previously been returned by
      *            {@link #onSaveInstanceState}.
      * @see #onSaveInstanceState
@@ -1856,17 +1856,17 @@
         public BaseSavedState(Parcelable superState) {
             super(superState);
         }
-        
+
         public static final Parcelable.Creator<BaseSavedState> CREATOR =
                 new Parcelable.Creator<BaseSavedState>() {
-            public BaseSavedState createFromParcel(Parcel in) {
-                return new BaseSavedState(in);
-            }
+                    public BaseSavedState createFromParcel(Parcel in) {
+                        return new BaseSavedState(in);
+                    }
 
-            public BaseSavedState[] newArray(int size) {
-                return new BaseSavedState[size];
-            }
-        };
+                    public BaseSavedState[] newArray(int size) {
+                        return new BaseSavedState[size];
+                    }
+                };
     }
 
 }
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 1ec00db..11b9606 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -142,13 +142,13 @@
         final boolean zenMuted = isZenMuted();
         mSeekBar.setEnabled(!zenMuted);
         if (zenMuted) {
-            mSeekBar.setProgress(mLastAudibleStreamVolume);
+            mSeekBar.setProgress(mLastAudibleStreamVolume, true);
         } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            mSeekBar.setProgress(0);
+            mSeekBar.setProgress(0, true);
         } else if (mMuted) {
-            mSeekBar.setProgress(0);
+            mSeekBar.setProgress(0, true);
         } else {
-            mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
+            mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume, true);
         }
     }
 
@@ -313,13 +313,13 @@
 
     public void muteVolume() {
         if (mVolumeBeforeMute != -1) {
-            mSeekBar.setProgress(mVolumeBeforeMute);
+            mSeekBar.setProgress(mVolumeBeforeMute, true);
             postSetVolume(mVolumeBeforeMute);
             postStartSample();
             mVolumeBeforeMute = -1;
         } else {
             mVolumeBeforeMute = mSeekBar.getProgress();
-            mSeekBar.setProgress(0);
+            mSeekBar.setProgress(0, true);
             postStopSample();
             postSetVolume(0);
         }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index c495e6c..a1763c0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5166,7 +5166,7 @@
      * </table>
      */
     public static final class PhoneLookup implements BaseColumns, PhoneLookupColumns,
-            ContactsColumns, ContactOptionsColumns {
+            ContactsColumns, ContactOptionsColumns, ContactNameColumns {
         /**
          * This utility class cannot be instantiated
          */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b05f3c2..f8cf9ab 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5947,6 +5947,36 @@
                 INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
 
         /**
+         * What happens when the user presses the Back button while in-call
+         * and the screen is on.<br/>
+         * <b>Values:</b><br/>
+         * 0 - The Back buttons does nothing different.<br/>
+         * 1 - The Back button hangs up the current call.<br/>
+         *
+         * @hide
+         */
+        public static final String INCALL_BACK_BUTTON_BEHAVIOR = "incall_back_button_behavior";
+
+        /**
+         * INCALL_BACK_BUTTON_BEHAVIOR value for no action.
+         * @hide
+         */
+        public static final int INCALL_BACK_BUTTON_BEHAVIOR_NONE = 0x0;
+
+        /**
+         * INCALL_BACK_BUTTON_BEHAVIOR value for "hang up".
+         * @hide
+         */
+        public static final int INCALL_BACK_BUTTON_BEHAVIOR_HANGUP = 0x1;
+
+        /**
+         * INCALL_POWER_BUTTON_BEHAVIOR default value.
+         * @hide
+         */
+        public static final int INCALL_BACK_BUTTON_BEHAVIOR_DEFAULT =
+                INCALL_BACK_BUTTON_BEHAVIOR_NONE;
+
+        /**
          * Whether the device should wake when the wake gesture sensor detects motion.
          * @hide
          */
@@ -7528,6 +7558,12 @@
               "network_switch_notification_rate_limit_millis";
 
        /**
+        * Whether to automatically switch away from wifi networks that lose Internet access.
+        * @hide
+        */
+       public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+
+       /**
         * Whether Wifi display is enabled/disabled
         * 0=disabled. 1=enabled.
         * @hide
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 9d4b0a4..c8358a6 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
 
@@ -126,6 +127,42 @@
     }
 
     /**
+     * Request that the provider be rebound, after a previous call to (@link requestUnbind).
+     *
+     * <p>This method will fail for providers that have not been granted the permission by the user.
+     */
+    public static final void requestRebind(ComponentName componentName) {
+        INotificationManager noMan = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        try {
+            noMan.requestBindProvider(componentName);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request that the provider service be unbound.
+     *
+     * <p>This will no longer receive subscription updates and will not be able to update the
+     * state of conditions until {@link #requestRebind(ComponentName)} is called.
+     * The service will likely be killed by the system after this call.
+     *
+     * <p>The service should wait for the {@link #onConnected()} event before performing this
+     * operation.
+     */
+    public final void requestUnbind() {
+        INotificationManager noMan = getNotificationInterface();
+        try {
+            noMan.requestUnbindProvider(mProvider);
+            // Disable future messages.
+            mProvider = null;
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Informs the notification manager that the state of a Condition has changed. Use this method
      * to put the system into Do Not Disturb mode or request that it exits Do Not Disturb mode. This
      * call will be ignored unless there is an enabled {@link android.app.AutomaticZenRule} owned by
@@ -193,6 +230,9 @@
         @Override
         public void handleMessage(Message msg) {
             String name = null;
+            if (!isBound()) {
+                return;
+            }
             try {
                 switch(msg.what) {
                     case ON_CONNECTED:
diff --git a/core/java/android/service/notification/NotificationRankerService.java b/core/java/android/service/notification/NotificationRankerService.java
index ee5361a..261d82d 100644
--- a/core/java/android/service/notification/NotificationRankerService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -99,6 +99,9 @@
     /** Autobundled summary notification was canceled because its group was unbundled */
     public static final int REASON_UNAUTOBUNDLED = 16;
 
+    /** Notification was canceled by the user banning the channel. */
+    public static final int REASON_CHANNEL_BANNED = 17;
+
     private Handler mHandler;
 
     /** @hide */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 474a9b6..b225018 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1,7 +1,7 @@
 /**
  * Copyright (c) 2014, The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License,  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
  *
@@ -398,18 +398,13 @@
         }
     }
 
-    public static ZenModeConfig readXml(XmlPullParser parser, Migration migration)
+    public static ZenModeConfig readXml(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         int type = parser.getEventType();
         if (type != XmlPullParser.START_TAG) return null;
         String tag = parser.getName();
         if (!ZEN_TAG.equals(tag)) return null;
         final ZenModeConfig rt = new ZenModeConfig();
-        final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
-        if (version == 1) {
-            final XmlV1 v1 = XmlV1.readXml(parser);
-            return migration.migrate(v1);
-        }
         rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
             tag = parser.getName();
@@ -1241,122 +1236,6 @@
         };
     }
 
-    // Legacy config
-    public static final class XmlV1 {
-        public static final String SLEEP_MODE_NIGHTS = "nights";
-        public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
-        public static final String SLEEP_MODE_DAYS_PREFIX = "days:";
-
-        private static final String EXIT_CONDITION_TAG = "exitCondition";
-        private static final String EXIT_CONDITION_ATT_COMPONENT = "component";
-        private static final String SLEEP_TAG = "sleep";
-        private static final String SLEEP_ATT_MODE = "mode";
-        private static final String SLEEP_ATT_NONE = "none";
-
-        private static final String SLEEP_ATT_START_HR = "startHour";
-        private static final String SLEEP_ATT_START_MIN = "startMin";
-        private static final String SLEEP_ATT_END_HR = "endHour";
-        private static final String SLEEP_ATT_END_MIN = "endMin";
-
-        public boolean allowCalls;
-        public boolean allowMessages;
-        public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
-        public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
-        public int allowFrom = SOURCE_ANYONE;
-
-        public String sleepMode;     // nights, weeknights, days:1,2,3  Calendar.days
-        public int sleepStartHour;   // 0-23
-        public int sleepStartMinute; // 0-59
-        public int sleepEndHour;
-        public int sleepEndMinute;
-        public boolean sleepNone;    // false = priority, true = none
-        public ComponentName[] conditionComponents;
-        public Uri[] conditionIds;
-        public Condition exitCondition;  // manual exit condition
-        public ComponentName exitConditionComponent;  // manual exit condition component
-
-        private static boolean isValidSleepMode(String sleepMode) {
-            return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
-                    || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null;
-        }
-
-        public static int[] tryParseDays(String sleepMode) {
-            if (sleepMode == null) return null;
-            sleepMode = sleepMode.trim();
-            if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS;
-            if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS;
-            if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null;
-            if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null;
-            return tryParseDayList(sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()), ",");
-        }
-
-        public static XmlV1 readXml(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            int type;
-            String tag;
-            XmlV1 rt = new XmlV1();
-            final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>();
-            final ArrayList<Uri> conditionIds = new ArrayList<Uri>();
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                tag = parser.getName();
-                if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
-                    if (!conditionComponents.isEmpty()) {
-                        rt.conditionComponents = conditionComponents
-                                .toArray(new ComponentName[conditionComponents.size()]);
-                        rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]);
-                    }
-                    return rt;
-                }
-                if (type == XmlPullParser.START_TAG) {
-                    if (ALLOW_TAG.equals(tag)) {
-                        rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
-                        rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
-                        rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
-                                DEFAULT_ALLOW_REMINDERS);
-                        rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS,
-                                DEFAULT_ALLOW_EVENTS);
-                        rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE);
-                        if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) {
-                            throw new IndexOutOfBoundsException("bad source in config:"
-                                    + rt.allowFrom);
-                        }
-                    } else if (SLEEP_TAG.equals(tag)) {
-                        final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
-                        rt.sleepMode = isValidSleepMode(mode)? mode : null;
-                        rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false);
-                        final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
-                        final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
-                        final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
-                        final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0);
-                        rt.sleepStartHour = isValidHour(startHour) ? startHour : 0;
-                        rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0;
-                        rt.sleepEndHour = isValidHour(endHour) ? endHour : 0;
-                        rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0;
-                    } else if (CONDITION_TAG.equals(tag)) {
-                        final ComponentName component =
-                                safeComponentName(parser, CONDITION_ATT_COMPONENT);
-                        final Uri conditionId = safeUri(parser, CONDITION_ATT_ID);
-                        if (component != null && conditionId != null) {
-                            conditionComponents.add(component);
-                            conditionIds.add(conditionId);
-                        }
-                    } else if (EXIT_CONDITION_TAG.equals(tag)) {
-                        rt.exitCondition = readConditionXml(parser);
-                        if (rt.exitCondition != null) {
-                            rt.exitConditionComponent =
-                                    safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
-                        }
-                    }
-                }
-            }
-            throw new IllegalStateException("Failed to reach END_DOCUMENT");
-        }
-    }
-
-    public interface Migration {
-        ZenModeConfig migrate(XmlV1 v1);
-    }
-
     public static class Diff {
         private final ArrayList<String> lines = new ArrayList<>();
 
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 55df206..ce3e282 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -279,7 +279,9 @@
                 if (width < 0.0f) break;
                 i--;
             }
-            while (i < limit - 1 && mChars[i + 1] == ' ') i++;
+            while (i < limit - 1 && (mChars[i + 1] == ' ' || w[i + 1] == 0.0f)) {
+                i++;
+            }
             return limit - i - 1;
         }
     }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 70d183d..bdbe8b0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1029,8 +1029,10 @@
 
                 for (i = len; i > 0; i--) {
                     float w = widths[i - 1 + lineStart - widthStart];
-
                     if (w + sum + ellipsisWidth > avail) {
+                        while (i < len && widths[i + lineStart - widthStart] == 0.0f) {
+                            i++;
+                        }
                         break;
                     }
 
@@ -1076,9 +1078,11 @@
                     float w = widths[right - 1 + lineStart - widthStart];
 
                     if (w + rsum > ravail) {
+                        while (right < len && widths[right + lineStart - widthStart] == 0.0f) {
+                            right++;
+                        }
                         break;
                     }
-
                     rsum += w;
                 }
 
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 16f3bc0..479f493 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -185,25 +185,25 @@
 
         final ViewGroup sceneRoot = scene.getSceneRoot();
         if (!sPendingTransitions.contains(sceneRoot)) {
-            sPendingTransitions.add(sceneRoot);
+            if (transition == null) {
+                scene.enter();
+            } else {
+                sPendingTransitions.add(sceneRoot);
 
-            Transition transitionClone = null;
-            if (transition != null) {
-                transitionClone = transition.clone();
+                Transition transitionClone = transition.clone();
                 transitionClone.setSceneRoot(sceneRoot);
+
+                Scene oldScene = Scene.getCurrentScene(sceneRoot);
+                if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
+                    transitionClone.setCanRemoveViews(true);
+                }
+
+                sceneChangeSetup(sceneRoot, transitionClone);
+
+                scene.enter();
+
+                sceneChangeRunTransition(sceneRoot, transitionClone);
             }
-
-            Scene oldScene = Scene.getCurrentScene(sceneRoot);
-            if (oldScene != null && transitionClone != null &&
-                    oldScene.isCreatedFromLayoutResource()) {
-                transitionClone.setCanRemoveViews(true);
-            }
-
-            sceneChangeSetup(sceneRoot, transitionClone);
-
-            scene.enter();
-
-            sceneChangeRunTransition(sceneRoot, transitionClone);
         }
     }
 
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index b0f15b5..a394f35 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -377,6 +377,10 @@
      * The object is intended to provide local information about the drag and drop operation. For
      * example, it can indicate whether the drag and drop operation is a copy or a move.
      * <p>
+     * The local state is available only to views in the activity which has started the drag
+     * operation. In all other activities this method will return null
+     * </p>
+     * <p>
      *  This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
      * </p>
      * @return The local state object sent to the system by startDrag().
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 91d8c24..c163393 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -29,6 +29,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IRemoteCallback;
+import android.os.ParcelFileDescriptor;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IDockedStackListener;
@@ -101,7 +102,7 @@
      * @param launchTaskBehind True if the token is been launched from behind.
      * @param taskBounds Bounds to use when creating a new Task with the input task Id if
      *                   the task doesn't exist yet.
-     * @param configuration Configuration that is being used with this task.
+     * @param overrideConfig Override configuration that is being used with this task.
      * @param taskResizeMode The resize mode of the task.
      * @param alwaysFocusable True if the app windows are always focusable regardless of the stack
      *                        they are in.
@@ -112,7 +113,7 @@
     void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
-            in Rect taskBounds, in Configuration configuration, int taskResizeMode,
+            in Rect taskBounds, in Configuration overrideConfig, int taskResizeMode,
             boolean alwaysFocusable, boolean homeTask, int targetSdkVersion,
             int rotationAnimationHint, boolean isOnTopLauncher);
     /**
@@ -123,13 +124,14 @@
      *                if the task doesn't exist yet.
      * @param taskBounds Bounds to use when creating a new Task with the input task Id if
      *                   the task doesn't exist yet.
-     * @param config Configuration that is being used with this task.
+     * @param overrideConfig Override configuration that is being used with this task.
      * @param taskResizeMode The resize mode of the task.
      * @param homeTask True if this is the task.
      * @param isOnTopLauncher True if this task is an on-top launcher.
      */
     void setAppTask(IBinder token, int taskId, int stackId, in Rect taskBounds,
-            in Configuration config, int taskResizeMode, boolean homeTask, boolean isOnTopLauncher);
+            in Configuration overrideConfig, int taskResizeMode, boolean homeTask,
+            boolean isOnTopLauncher);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
     void setFocusedApp(IBinder token, boolean moveFocusNow);
@@ -255,6 +257,13 @@
     void setScreenCaptureDisabled(int userId, boolean disabled);
 
     /**
+     * Testing and debugging infrastructure for writing surface events
+     * to given FD. See RemoteSurfaceTrace.java or Wm.java for format.
+     */
+    void enableSurfaceTrace(in ParcelFileDescriptor fd);
+    void disableSurfaceTrace();
+
+    /**
      * Cancels the window transitions for the given task.
      */
     void cancelTaskWindowTransition(int taskId);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 3e72192..9019e87 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.TestApi;
 import android.graphics.Matrix;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -2355,6 +2356,7 @@
      * @see #getActionButton()
      * @hide
      */
+    @TestApi
     public final void setActionButton(int button) {
         nativeSetActionButton(mNativePtr, button);
     }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index f92d83a..9f46f3f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -33,6 +33,18 @@
 
 /**
  * Handle onto a raw buffer that is being managed by the screen compositor.
+ *
+ * <p>A Surface is generally created by or from a consumer of image buffers (such as a
+ * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or
+ * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as
+ * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},
+ * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw
+ * into.</p>
+ *
+ * <p><strong>Note:</strong> A Surface acts like a
+ * {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By
+ * itself it will not keep its parent consumer from being reclaimed.</p>
  */
 public class Surface implements Parcelable {
     private static final String TAG = "Surface";
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e778a7f..6456826 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -308,6 +308,17 @@
         mCloseGuard.open("release");
     }
 
+    // This is a transfer constructor, useful for transferring a live SurfaceControl native
+    // object to another Java wrapper which could have some different behavior, e.g.
+    // event logging.
+    public SurfaceControl(SurfaceControl other) {
+        mName = other.mName;
+        mNativeObject = other.mNativeObject;
+        other.mCloseGuard.close();
+        other.mNativeObject = 0;
+        mCloseGuard.open("release");
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fecb9e6..a7337d2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19102,7 +19102,7 @@
             return mAttachInfo.mTreeObserver;
         }
         if (mFloatingTreeObserver == null) {
-            mFloatingTreeObserver = new ViewTreeObserver();
+            mFloatingTreeObserver = new ViewTreeObserver(mContext);
         }
         return mFloatingTreeObserver;
     }
@@ -20657,8 +20657,10 @@
      * @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the
      * drag shadow.
      * @param myLocalState An {@link java.lang.Object} containing local data about the drag and
-     * drop operation. This Object is put into every DragEvent object sent by the system during the
-     * current drag.
+     * drop operation. When dispatching drag events to views in the same activity this object
+     * will be available through {@link android.view.DragEvent#getLocalState()}. Views in other
+     * activities will not have access to this data ({@link android.view.DragEvent#getLocalState()}
+     * will return null).
      * <p>
      * myLocalState is a lightweight mechanism for the sending information from the dragged View
      * to the target Views. For example, it can contain flags that differentiate between a
@@ -23051,7 +23053,7 @@
          * The view tree observer used to dispatch global events like
          * layout, pre-draw, touch mode change, etc.
          */
-        final ViewTreeObserver mTreeObserver = new ViewTreeObserver();
+        final ViewTreeObserver mTreeObserver;
 
         /**
          * A Canvas used by the view hierarchy to perform bitmap caching.
@@ -23173,7 +23175,8 @@
          * @param handler the events handler the view must use
          */
         AttachInfo(IWindowSession session, IWindow window, Display display,
-                ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
+                ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,
+                Context context) {
             mSession = session;
             mWindow = window;
             mWindowToken = window.asBinder();
@@ -23181,6 +23184,7 @@
             mViewRootImpl = viewRootImpl;
             mHandler = handler;
             mRootCallbacks = effectPlayer;
+            mTreeObserver = new ViewTreeObserver(context);
         }
     }
 
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 6f9768f..e1ff0d6 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -846,6 +846,8 @@
         view.post(new Runnable() {
             @Override
             public void run() {
+                encoder.addProperty("window:left", view.mAttachInfo.mWindowLeft);
+                encoder.addProperty("window:top", view.mAttachInfo.mWindowTop);
                 view.encode(encoder);
                 latch.countDown();
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ece0e1b..3bbf75e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -429,7 +429,8 @@
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
         mAdded = false;
-        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
+        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
+                context);
         mAccessibilityManager = AccessibilityManager.getInstance(context);
         mAccessibilityInteractionConnectionManager =
             new AccessibilityInteractionConnectionManager();
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 521fd31..7dd2fc2 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -16,8 +16,11 @@
 
 package android.view;
 
+import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Build;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -49,7 +52,9 @@
     private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
 
     // These listeners cannot be mutated during dispatch
+    private boolean mInDispatchOnDraw;
     private ArrayList<OnDrawListener> mOnDrawListeners;
+    private static boolean sIllegalOnDrawModificationIsFatal;
 
     /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
      * that the listener will be immediately called. */
@@ -327,7 +332,9 @@
     /**
      * Creates a new ViewTreeObserver. This constructor should not be called
      */
-    ViewTreeObserver() {
+    ViewTreeObserver(Context context) {
+        sIllegalOnDrawModificationIsFatal =
+                context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.N_MR1;
     }
 
     /**
@@ -657,6 +664,15 @@
             mOnDrawListeners = new ArrayList<OnDrawListener>();
         }
 
+        if (mInDispatchOnDraw) {
+            IllegalStateException ex = new IllegalStateException(
+                    "Cannot call addOnDrawListener inside of onDraw");
+            if (sIllegalOnDrawModificationIsFatal) {
+                throw ex;
+            } else {
+                Log.e("ViewTreeObserver", ex.getMessage(), ex);
+            }
+        }
         mOnDrawListeners.add(listener);
     }
 
@@ -676,6 +692,15 @@
         if (mOnDrawListeners == null) {
             return;
         }
+        if (mInDispatchOnDraw) {
+            IllegalStateException ex = new IllegalStateException(
+                    "Cannot call removeOnDrawListener inside of onDraw");
+            if (sIllegalOnDrawModificationIsFatal) {
+                throw ex;
+            } else {
+                Log.e("ViewTreeObserver", ex.getMessage(), ex);
+            }
+        }
         mOnDrawListeners.remove(victim);
     }
 
@@ -976,11 +1001,13 @@
      */
     public final void dispatchOnDraw() {
         if (mOnDrawListeners != null) {
+            mInDispatchOnDraw = true;
             final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
             int numListeners = listeners.size();
             for (int i = 0; i < numListeners; ++i) {
                 listeners.get(i).onDraw();
             }
+            mInDispatchOnDraw = false;
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 18e2118..9127861 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -19,6 +19,7 @@
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -554,7 +555,7 @@
      */
     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
 
-    private static final AtomicInteger sNumInstancesInUse = new AtomicInteger();
+    private static AtomicInteger sNumInstancesInUse;
 
     /**
      * Gets the accessibility view id which identifies a View in the view three.
@@ -2692,7 +2693,9 @@
      */
     public static AccessibilityNodeInfo obtain() {
         AccessibilityNodeInfo info = sPool.acquire();
-        sNumInstancesInUse.incrementAndGet();
+        if (sNumInstancesInUse != null) {
+            sNumInstancesInUse.incrementAndGet();
+        }
         return (info != null) ? info : new AccessibilityNodeInfo();
     }
 
@@ -2720,16 +2723,19 @@
     public void recycle() {
         clear();
         sPool.release(this);
-        sNumInstancesInUse.decrementAndGet();
+        if (sNumInstancesInUse != null) {
+            sNumInstancesInUse.decrementAndGet();
+        }
     }
 
     /**
-     * @return The number of instances of this class that have been obtained but not recycled.
+     * Specify a counter that will be incremented on obtain() and decremented on recycle()
      *
      * @hide
      */
-    public static int getNumInstancesInUse() {
-        return sNumInstancesInUse.get();
+    @TestApi
+    public static void setNumInstancesInUseCounter(AtomicInteger counter) {
+        sNumInstancesInUse = counter;
     }
 
     /**
@@ -3750,7 +3756,7 @@
          * how to override the standard click action by adding a custom label:
          * <pre>
          *   AccessibilityAction action = new AccessibilityAction(
-         *           AccessibilityAction.ACTION_ACTION_CLICK, getLocalizedLabel());
+         *           AccessibilityAction.ACTION_CLICK.getId(), getLocalizedLabel());
          *   node.addAction(action);
          * </pre>
          *
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 8e308d6..3287298 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -17,6 +17,7 @@
 package android.view.accessibility;
 
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -83,7 +84,7 @@
     private static final int MAX_POOL_SIZE = 10;
     private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
             new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
-    private static final AtomicInteger sNumInstancesInUse = new AtomicInteger();
+    private static AtomicInteger sNumInstancesInUse;
 
     // Data.
     private int mType = UNDEFINED;
@@ -403,7 +404,9 @@
         if (info == null) {
             info = new AccessibilityWindowInfo();
         }
-        sNumInstancesInUse.incrementAndGet();
+        if (sNumInstancesInUse != null) {
+            sNumInstancesInUse.incrementAndGet();
+        }
         return info;
     }
 
@@ -441,12 +444,15 @@
     }
 
     /**
-     * @return The number of instances of this class that have been obtained but not recycled.
+     * Specify a counter that will be incremented on obtain() and decremented on recycle()
      *
      * @hide
      */
-    public static int getNumInstancesInUse() {
-        return sNumInstancesInUse.get();
+    @TestApi
+    public static void setNumInstancesInUseCounter(AtomicInteger counter) {
+        if (sNumInstancesInUse != null) {
+            sNumInstancesInUse = counter;
+        }
     }
 
     /**
@@ -460,7 +466,9 @@
     public void recycle() {
         clear();
         sPool.release(this);
-        sNumInstancesInUse.decrementAndGet();
+        if (sNumInstancesInUse != null) {
+            sNumInstancesInUse.decrementAndGet();
+        }
     }
 
     @Override
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionCallback.aidl
deleted file mode 100644
index eeab4f2..0000000
--- a/core/java/android/view/accessibility/IAccessibilityInteractionCallback.aidl
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.accessibility;
-
-import android.view.accessibility.AccessibilityNodeInfo;
-import java.util.List;
-
-/**
- * Callback for specifying the result for an asynchronous request made
- * via calling a method on IAccessibilityInteractionCallback.
- *
- * @hide
- */
-oneway interface IAccessibilityInteractionCallback {
-
-    /**
-     * Sets the result of an async request that returns an {@link AccessibilityNodeInfo}.
-     *
-     * @param infos The result {@link AccessibilityNodeInfo}.
-     * @param interactionId The interaction id to match the result with the request.
-     */
-    void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
-
-    /**
-     * Sets the result of an async request that returns {@link AccessibilityNodeInfo}s.
-     *
-     * @param infos The result {@link AccessibilityNodeInfo}s.
-     * @param interactionId The interaction id to match the result with the request.
-     */
-    void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
-        int interactionId);
-
-    /**
-     * Sets the result of a request to perform an accessibility action.
-     *
-     * @param Whether the action was performed.
-     * @param interactionId The interaction id to match the result with the request.
-     */
-    void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
-}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index 42ae1b3..c1a3ab7 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -16,7 +16,6 @@
 
 package android.view.accessibility;
 
-import android.graphics.Point;
 import android.view.accessibility.AccessibilityNodeInfo;
 import java.util.List;
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e37cf96..ca370dc 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2065,12 +2065,10 @@
      * have any input method subtype.
      */
     public InputMethodSubtype getCurrentInputMethodSubtype() {
-        synchronized (mH) {
-            try {
-                return mService.getCurrentInputMethodSubtype();
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+        try {
+            return mService.getCurrentInputMethodSubtype();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 119b8ed..184453b 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -7613,15 +7613,27 @@
                 final int position = mTargetPos;
                 final int lastPos = firstPos + childCount - 1;
 
-                int viewTravelCount = 0;
+                // Account for the visible "portion" of the first / last child when we estimate
+                // how many screens we should travel to reach our target
+                final View firstChild = getChildAt(0);
+                final int firstChildHeight = firstChild.getHeight();
+                final View lastChild = getChildAt(childCount - 1);
+                final int lastChildHeight = lastChild.getHeight();
+                final float firstPositionVisiblePart = (firstChildHeight == 0.0f) ? 1.0f
+                        : (float) (firstChildHeight + firstChild.getTop()) / firstChildHeight;
+                final float lastPositionVisiblePart = (lastChildHeight == 0.0f) ? 1.0f
+                        : (float) (lastChildHeight + getHeight() - lastChild.getBottom())
+                                / lastChildHeight;
+
+                float viewTravelCount = 0;
                 if (position < firstPos) {
-                    viewTravelCount = firstPos - position + 1;
+                    viewTravelCount = firstPos - position + (1.0f - firstPositionVisiblePart) + 1;
                 } else if (position > lastPos) {
-                    viewTravelCount = position - lastPos;
+                    viewTravelCount = position - lastPos + (1.0f - lastPositionVisiblePart);
                 }
 
                 // Estimate how many screens we should travel
-                final float screenTravelCount = (float) viewTravelCount / childCount;
+                final float screenTravelCount = viewTravelCount / childCount;
 
                 final float modifier = Math.min(Math.abs(screenTravelCount), 1.f);
                 if (position < firstPos) {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 82f5f01..72b2e31 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.TestApi;
@@ -30,10 +32,12 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.text.Editable;
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.TextWatcher;
 import android.text.method.NumberKeyListener;
 import android.util.AttributeSet;
 import android.util.SparseArray;
@@ -53,9 +57,6 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 
-import com.android.internal.R;
-import libcore.icu.LocaleData;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -63,6 +64,8 @@
 import java.util.List;
 import java.util.Locale;
 
+import libcore.icu.LocaleData;
+
 /**
  * A widget that enables the user to select a number from a predefined range.
  * There are two flavors of this widget and which one is presented to the user
@@ -2008,7 +2011,7 @@
             removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
         }
         if (mSetSelectionCommand != null) {
-            removeCallbacks(mSetSelectionCommand);
+            mSetSelectionCommand.cancel();
         }
         if (mBeginSoftInputOnLongPressCommand != null) {
             removeCallbacks(mBeginSoftInputOnLongPressCommand);
@@ -2050,18 +2053,14 @@
     }
 
     /**
-     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
-     * </code> to <code>selectionEnd</code>.
+     * Posts a {@link SetSelectionCommand} from the given
+     * {@code selectionStart} to {@code selectionEnd}.
      */
     private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
         if (mSetSelectionCommand == null) {
-            mSetSelectionCommand = new SetSelectionCommand();
-        } else {
-            removeCallbacks(mSetSelectionCommand);
+            mSetSelectionCommand = new SetSelectionCommand(mInputText);
         }
-        mSetSelectionCommand.mSelectionStart = selectionStart;
-        mSetSelectionCommand.mSelectionEnd = selectionEnd;
-        post(mSetSelectionCommand);
+        mSetSelectionCommand.post(selectionStart, selectionEnd);
     }
 
     /**
@@ -2107,6 +2106,12 @@
         @Override
         public CharSequence filter(
                 CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
+            // We don't know what the output will be, so always cancel any
+            // pending set selection command.
+            if (mSetSelectionCommand != null) {
+                mSetSelectionCommand.cancel();
+            }
+
             if (mDisplayedValues == null) {
                 CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
                 if (filtered == null) {
@@ -2254,12 +2259,39 @@
     /**
      * Command for setting the input text selection.
      */
-    class SetSelectionCommand implements Runnable {
-        private int mSelectionStart;
+    private static class SetSelectionCommand implements Runnable {
+        private final EditText mInputText;
 
+        private int mSelectionStart;
         private int mSelectionEnd;
 
+        /** Whether this runnable is currently posted. */
+        private boolean mPosted;
+
+        public SetSelectionCommand(EditText inputText) {
+            mInputText = inputText;
+        }
+
+        public void post(int selectionStart, int selectionEnd) {
+            mSelectionStart = selectionStart;
+            mSelectionEnd = selectionEnd;
+
+            if (!mPosted) {
+                mInputText.post(this);
+                mPosted = true;
+            }
+        }
+
+        public void cancel() {
+            if (mPosted) {
+                mInputText.removeCallbacks(this);
+                mPosted = false;
+            }
+        }
+
+        @Override
         public void run() {
+            mPosted = false;
             mInputText.setSelection(mSelectionStart, mSelectionEnd);
         }
     }
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 50569d7..9938789 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -89,8 +89,9 @@
      * means no bounce. This behavior is no longer supported and this coefficient has no effect.
      * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. This
      * behavior is no longer supported and this coefficient has no effect.
-     * !deprecated Use {!link #OverScroller(Context, Interpolator, boolean)} instead.
+     * @deprecated Use {@link #OverScroller(Context, Interpolator)} instead.
      */
+    @Deprecated
     public OverScroller(Context context, Interpolator interpolator,
             float bounceCoefficientX, float bounceCoefficientY) {
         this(context, interpolator, true);
@@ -107,8 +108,9 @@
      * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. This
      * behavior is no longer supported and this coefficient has no effect.
      * @param flywheel If true, successive fling motions will keep on increasing scroll speed.
-     * !deprecated Use {!link OverScroller(Context, Interpolator, boolean)} instead.
+     * @deprecated Use {@link #OverScroller(Context, Interpolator)} instead.
      */
+    @Deprecated
     public OverScroller(Context context, Interpolator interpolator,
             float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
         this(context, interpolator, flywheel);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index c92ee15..5e73a63 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1758,11 +1758,22 @@
      */
     public int getMaxAvailableHeight(
             @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
-        final Rect displayFrame = new Rect();
+        Rect displayFrame = null;
+        final Rect visibleDisplayFrame = new Rect();
+
+        anchor.getWindowVisibleDisplayFrame(visibleDisplayFrame);
         if (ignoreBottomDecorations) {
+            // In the ignore bottom decorations case we want to
+            // still respect all other decorations so we use the inset visible
+            // frame on the top right and left and take the bottom
+            // value from the full frame.
+            displayFrame = new Rect();
             anchor.getWindowDisplayFrame(displayFrame);
+            displayFrame.top = visibleDisplayFrame.top;
+            displayFrame.right = visibleDisplayFrame.right;
+            displayFrame.left = visibleDisplayFrame.left;
         } else {
-            anchor.getWindowVisibleDisplayFrame(displayFrame);
+            displayFrame = visibleDisplayFrame;
         }
 
         final int[] anchorPos = mTmpDrawingLocation;
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 5878cad..9139361 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -816,9 +816,11 @@
 
         switch (heightMode) {
             case MeasureSpec.AT_MOST:
-            case MeasureSpec.UNSPECIFIED:
                 height = Math.min(getPreferredHeight(), height);
                 break;
+            case MeasureSpec.UNSPECIFIED:
+                height = getPreferredHeight();
+                break;
         }
         heightMode = MeasureSpec.EXACTLY;
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0a4ac0d..1e26c92 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1469,7 +1469,7 @@
                 boolean found = false;
                 // Only loop to the end of into as it was before we started; no dupes in from.
                 for (int j = 0; j < intoCount; j++) {
-                    final ResolvedComponentInfo rci = into.get(i);
+                    final ResolvedComponentInfo rci = into.get(j);
                     if (isSameResolvedComponent(newInfo, rci)) {
                         found = true;
                         rci.add(intent, newInfo);
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
index 3bd79f7e..d175202 100644
--- a/core/java/com/android/internal/os/WifiPowerEstimator.java
+++ b/core/java/com/android/internal/os/WifiPowerEstimator.java
@@ -38,13 +38,13 @@
     }
 
     /**
-     * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
+     * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
      */
     private static double getWifiPowerPerPacket(PowerProfile profile) {
         final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
         final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
                 / 3600;
-        return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
+        return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048);
     }
 
     @Override
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 40b9ab6..59a3563 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -631,6 +631,13 @@
         // an error.
         ZygoteHooks.startZygoteNoThreadCreation();
 
+        // Zygote goes into its own process group.
+        try {
+            Os.setpgid(0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to setpgid(0,0)", ex);
+        }
+
         try {
             Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
             RuntimeInit.enableDdms();
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 7d222c7..46098c5 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -108,7 +108,7 @@
         }
         if (stillScheduled) {
             Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
-            mHandler.handleMessage(msg);
+            mHandler.dispatchMessage(msg);
             msg.recycle();
         }
     }
diff --git a/core/java/com/android/internal/widget/WatchListDecorLayout.java b/core/java/com/android/internal/widget/WatchListDecorLayout.java
index 538ceca..5b49611 100644
--- a/core/java/com/android/internal/widget/WatchListDecorLayout.java
+++ b/core/java/com/android/internal/widget/WatchListDecorLayout.java
@@ -306,8 +306,9 @@
             if (mListView.getChildCount() > 0) {
                 if (mListView.getLastVisiblePosition() >= mListView.getCount() - 1) {
                     View lastChild = mListView.getChildAt(mListView.getChildCount() - 1);
-                    setScrolling(mBottomPanel,
-                            lastChild.getY() + lastChild.getHeight() - mBottomPanel.getTop());
+                    setScrolling(mBottomPanel, Math.max(
+                            0,
+                            lastChild.getY() + lastChild.getHeight() - mBottomPanel.getTop()));
                 } else {
                     // shift to hide the frame, last child is not the last position
                     setScrolling(mBottomPanel, mBottomPanel.getHeight());
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index cd49d02..4c6350b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -124,7 +124,6 @@
     android/graphics/PathMeasure.cpp \
     android/graphics/PathEffect.cpp \
     android/graphics/Picture.cpp \
-    android/graphics/PorterDuff.cpp \
     android/graphics/BitmapRegionDecoder.cpp \
     android/graphics/Rasterizer.cpp \
     android/graphics/Region.cpp \
@@ -132,7 +131,6 @@
     android/graphics/SurfaceTexture.cpp \
     android/graphics/Typeface.cpp \
     android/graphics/Utils.cpp \
-    android/graphics/Xfermode.cpp \
     android/graphics/YuvToJpegEncoder.cpp \
     android/graphics/pdf/PdfDocument.cpp \
     android/graphics/pdf/PdfEditor.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d75d53e..cdaa4dc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -127,11 +127,9 @@
 extern int register_android_graphics_Path(JNIEnv* env);
 extern int register_android_graphics_PathMeasure(JNIEnv* env);
 extern int register_android_graphics_Picture(JNIEnv*);
-extern int register_android_graphics_PorterDuff(JNIEnv* env);
 extern int register_android_graphics_Rasterizer(JNIEnv* env);
 extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
-extern int register_android_graphics_Xfermode(JNIEnv* env);
 extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
@@ -1343,13 +1341,11 @@
     REG_JNI(register_android_graphics_PathMeasure),
     REG_JNI(register_android_graphics_PathEffect),
     REG_JNI(register_android_graphics_Picture),
-    REG_JNI(register_android_graphics_PorterDuff),
     REG_JNI(register_android_graphics_Rasterizer),
     REG_JNI(register_android_graphics_Region),
     REG_JNI(register_android_graphics_Shader),
     REG_JNI(register_android_graphics_SurfaceTexture),
     REG_JNI(register_android_graphics_Typeface),
-    REG_JNI(register_android_graphics_Xfermode),
     REG_JNI(register_android_graphics_YuvImage),
     REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 2956175..18c4ee3 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -151,12 +151,12 @@
     mPixelRef->unref();
 }
 
-Bitmap::Bitmap(void* address, int fd,
+Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
         : mPixelStorageType(PixelStorageType::Ashmem) {
     mPixelStorage.ashmem.address = address;
     mPixelStorage.ashmem.fd = fd;
-    mPixelStorage.ashmem.size = ashmem_get_size_region(fd);
+    mPixelStorage.ashmem.size = mappedSize;
     mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
     // Note: this will trigger a call to onStrongRefDestroyed(), but
     // we want the pixel ref to have a ref count of 0 at this point
@@ -1027,7 +1027,7 @@
 
         // Map the pixels in place and take ownership of the ashmem region.
         nativeBitmap = GraphicsJNI::mapAshmemPixelRef(env, bitmap.get(),
-                ctable, dupFd, const_cast<void*>(blob.data()), !isMutable);
+                ctable, dupFd, const_cast<void*>(blob.data()), size, !isMutable);
         SkSafeUnref(ctable);
         if (!nativeBitmap) {
             close(dupFd);
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index eadba5c..aaea178 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -51,8 +51,8 @@
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
     Bitmap(void* address, void* context, FreeFunc freeFunc,
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
-    Bitmap(void* address, int fd, const SkImageInfo& info, size_t rowBytes,
-            SkColorTable* ctable);
+    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
+            size_t rowBytes, SkColorTable* ctable);
 
     const SkImageInfo& info() const;
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 695aed5..77799d6 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -384,9 +384,12 @@
     // Set the alpha type for the decode.
     SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
 
-    const SkImageInfo decodeInfo = codec->getInfo().makeWH(size.width(), size.height())
-                                                   .makeColorType(decodeColorType)
-                                                   .makeAlphaType(alphaType);
+    // Enable legacy behavior to avoid any gamma correction.  Android's assets are
+    // adjusted to expect a non-gamma correct premultiply.
+    sk_sp<SkColorSpace> colorSpace = nullptr;
+    const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType,
+                                                     alphaType, colorSpace);
+
     SkImageInfo bitmapInfo = decodeInfo;
     if (decodeColorType == kGray_8_SkColorType) {
         // The legacy implementation of BitmapFactory used kAlpha8 for
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index b5630d5..c6a51e8 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -613,7 +613,7 @@
         return nullptr;
     }
 
-    android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
+    android::Bitmap* wrapper = new android::Bitmap(addr, fd, size, info, rowBytes, ctable);
     wrapper->getSkBitmap(bitmap);
     // since we're already allocated, we lockPixels right away
     // HeapAllocator behaves this way too
@@ -623,7 +623,7 @@
 }
 
 android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
-        SkColorTable* ctable, int fd, void* addr, bool readOnly) {
+        SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly) {
     const SkImageInfo& info = bitmap->info();
     if (info.colorType() == kUnknown_SkColorType) {
         doThrowIAE(env, "unknown bitmap configuration");
@@ -633,7 +633,8 @@
     if (!addr) {
         // Map existing ashmem region if not already mapped.
         int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
-        addr = mmap(NULL, ashmem_get_size_region(fd), flags, MAP_SHARED, fd, 0);
+        size = ashmem_get_size_region(fd);
+        addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
         if (addr == MAP_FAILED) {
             return nullptr;
         }
@@ -643,7 +644,7 @@
     // attempting to compute our own.
     const size_t rowBytes = bitmap->rowBytes();
 
-    android::Bitmap* wrapper = new android::Bitmap(addr, fd, info, rowBytes, ctable);
+    android::Bitmap* wrapper = new android::Bitmap(addr, fd, size, info, rowBytes, ctable);
     wrapper->getSkBitmap(bitmap);
     if (readOnly) {
         bitmap->pixelRef()->setImmutable();
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 0f04f6d..89636db 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -100,7 +100,7 @@
             SkColorTable* ctable);
 
     static android::Bitmap* mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
-            SkColorTable* ctable, int fd, void* addr, bool readOnly);
+            SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly);
 
     /**
      * Given a bitmap we natively allocate a memory block to store the contents
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 9be659e..315dd6b 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -275,10 +275,30 @@
         return reinterpret_cast<jlong>(obj->setColorFilter(filter));
     }
 
-    static jlong setXfermode(JNIEnv* env, jobject clazz, jlong objHandle, jlong xfermodeHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        SkXfermode* xfermode = reinterpret_cast<SkXfermode*>(xfermodeHandle);
-        return reinterpret_cast<jlong>(obj->setXfermode(xfermode));
+    static void setXfermode(JNIEnv* env, jobject clazz, jlong paintHandle, jint xfermodeHandle) {
+        // validate that the Java enum values match our expectations
+        static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch");
+        static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch");
+        static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch");
+        static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch");
+        static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch");
+        static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch");
+        static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch");
+        static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch");
+        static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch");
+        static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch");
+        static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch");
+        static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch");
+        static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch");
+        static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch");
+        static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
+        static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch");
+        static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch");
+        static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch");
+
+        SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setXfermodeMode(mode);
     }
 
     static jlong setPathEffect(JNIEnv* env, jobject clazz, jlong objHandle, jlong effectHandle) {
@@ -961,7 +981,7 @@
     {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
     {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader},
     {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
-    {"nSetXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
+    {"nSetXfermode","!(JI)V", (void*) PaintGlue::setXfermode},
     {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
     {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
     {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
diff --git a/core/jni/android/graphics/PorterDuff.cpp b/core/jni/android/graphics/PorterDuff.cpp
deleted file mode 100644
index dfc3c9d..0000000
--- a/core/jni/android/graphics/PorterDuff.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* libs/android_runtime/android/graphics/PorterDuff.cpp
-**
-** Copyright 2006, 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.
-*/
-
-// This file was generated from the C++ include file: SkPorterDuff.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include "core_jni_helpers.h"
-
-#include "SkXfermode.h"
-
-namespace android {
-
-class SkPorterDuffGlue {
-public:
-
-    static jlong CreateXfermode(JNIEnv* env, jobject, jint modeHandle) {
-        // validate that the Java enum values match our expectations
-        static_assert(0  == SkXfermode::kClear_Mode,    "xfermode_mismatch");
-        static_assert(1  == SkXfermode::kSrc_Mode,      "xfermode_mismatch");
-        static_assert(2  == SkXfermode::kDst_Mode,      "xfermode_mismatch");
-        static_assert(3  == SkXfermode::kSrcOver_Mode,  "xfermode_mismatch");
-        static_assert(4  == SkXfermode::kDstOver_Mode,  "xfermode_mismatch");
-        static_assert(5  == SkXfermode::kSrcIn_Mode,    "xfermode_mismatch");
-        static_assert(6  == SkXfermode::kDstIn_Mode,    "xfermode_mismatch");
-        static_assert(7  == SkXfermode::kSrcOut_Mode,   "xfermode_mismatch");
-        static_assert(8  == SkXfermode::kDstOut_Mode,   "xfermode_mismatch");
-        static_assert(9  == SkXfermode::kSrcATop_Mode,  "xfermode_mismatch");
-        static_assert(10 == SkXfermode::kDstATop_Mode,  "xfermode_mismatch");
-        static_assert(11 == SkXfermode::kXor_Mode,      "xfermode_mismatch");
-        static_assert(16 == SkXfermode::kDarken_Mode,   "xfermode_mismatch");
-        static_assert(17 == SkXfermode::kLighten_Mode,  "xfermode_mismatch");
-        static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
-        static_assert(14 == SkXfermode::kScreen_Mode,   "xfermode_mismatch");
-        static_assert(12 == SkXfermode::kPlus_Mode,     "xfermode_mismatch");
-        static_assert(15 == SkXfermode::kOverlay_Mode,  "xfermode_mismatch");
-
-        SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
-        return reinterpret_cast<jlong>(SkXfermode::Create(mode));
-    }
-
-};
-
-static const JNINativeMethod methods[] = {
-    {"nativeCreateXfermode","(I)J", (void*) SkPorterDuffGlue::CreateXfermode},
-};
-
-int register_android_graphics_PorterDuff(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/graphics/PorterDuffXfermode", methods, NELEM(methods));
-}
-
-}
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index de32dd9..03462a6 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -1,5 +1,6 @@
 #include "GraphicsJNI.h"
 #include "SkGradientShader.h"
+#include "SkImagePriv.h"
 #include "SkShader.h"
 #include "SkXfermode.h"
 #include "core_jni_helpers.h"
@@ -94,12 +95,15 @@
         // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
         GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     }
-    SkShader* s = SkShader::CreateBitmapShader(bitmap,
-                                        (SkShader::TileMode)tileModeX,
-                                        (SkShader::TileMode)tileModeY);
+    sk_sp<SkShader> s = SkMakeBitmapShader(bitmap,
+                                           (SkShader::TileMode)tileModeX,
+                                           (SkShader::TileMode)tileModeY,
+                                           nullptr,
+                                           kNever_SkCopyPixelsMode,
+                                           nullptr);
 
-    ThrowIAE_IfNull(env, s);
-    return reinterpret_cast<jlong>(s);
+    ThrowIAE_IfNull(env, s.get());
+    return reinterpret_cast<jlong>(s.release());
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -225,24 +229,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static jlong ComposeShader_create1(JNIEnv* env, jobject o,
-        jlong shaderAHandle, jlong shaderBHandle, jlong modeHandle)
-{
-    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
-    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
-    SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
-    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, mode);
-    return reinterpret_cast<jlong>(shader);
-}
-
-static jlong ComposeShader_create2(JNIEnv* env, jobject o,
+static jlong ComposeShader_create(JNIEnv* env, jobject o,
         jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle)
 {
     SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
     SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
     SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
-    SkAutoTUnref<SkXfermode> xfermode(SkXfermode::Create(mode));
-    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, xfermode.get());
+    SkShader* shader = SkShader::CreateComposeShader(shaderA, shaderB, mode);
     return reinterpret_cast<jlong>(shader);
 }
 
@@ -278,8 +271,7 @@
 };
 
 static const JNINativeMethod gComposeShaderMethods[] = {
-    { "nativeCreate1",      "(JJJ)J",   (void*)ComposeShader_create1     },
-    { "nativeCreate2",      "(JJI)J",   (void*)ComposeShader_create2     },
+    { "nativeCreate",      "(JJI)J",   (void*)ComposeShader_create     },
 };
 
 int register_android_graphics_Shader(JNIEnv* env)
diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp
deleted file mode 100644
index 78975a4f..0000000
--- a/core/jni/android/graphics/Xfermode.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni.h"
-//#include "GraphicsJNI.h"
-#include "core_jni_helpers.h"
-
-#include <SkXfermode.h>
-
-namespace android {
-
-class SkXfermodeGlue {
-public:
-    static void finalizer(JNIEnv* env, jobject, jlong objHandle)
-    {
-        SkXfermode* obj = reinterpret_cast<SkXfermode *>(objHandle);
-        SkSafeUnref(obj);
-    }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const JNINativeMethod gXfermodeMethods[] = {
-    {"finalizer", "(J)V", (void*) SkXfermodeGlue::finalizer}
-};
-
-int register_android_graphics_Xfermode(JNIEnv* env) {
-    android::RegisterMethodsOrDie(env, "android/graphics/Xfermode", gXfermodeMethods,
-                                  NELEM(gXfermodeMethods));
-    return 0;
-}
-
-}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 9459257..b926270 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -350,9 +350,16 @@
     postData(msgType, dataPtr, NULL);
 }
 
-void JNICameraContext::postRecordingFrameHandleTimestamp(nsecs_t, native_handle_t*) {
-    // This is not needed at app layer. This should not be called because JNICameraContext cannot
-    // start video recording.
+void JNICameraContext::postRecordingFrameHandleTimestamp(nsecs_t, native_handle_t* handle) {
+    // Video buffers are not needed at app layer so just return the video buffers here.
+    // This may be called when stagefright just releases camera but there are still outstanding
+    // video buffers.
+    if (mCamera != nullptr) {
+        mCamera->releaseRecordingFrameHandle(handle);
+    } else {
+        native_handle_close(handle);
+        native_handle_delete(handle);
+    }
 }
 
 void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata)
diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp
index 399e7b1..4cbe3e4 100644
--- a/core/jni/android_hardware_UsbRequest.cpp
+++ b/core/jni/android_hardware_UsbRequest.cpp
@@ -47,7 +47,7 @@
     struct usb_device* device = get_device_from_object(env, java_device);
     if (!device) {
         ALOGE("device null in native_init");
-        return false;
+        return JNI_FALSE;
     }
 
     // construct an endpoint descriptor from the Java object fields
@@ -83,13 +83,13 @@
     struct usb_request* request = get_request_from_object(env, thiz);
     if (!request) {
         ALOGE("request is closed in native_queue");
-        return false;
+        return JNI_FALSE;
     }
 
     if (buffer && length) {
         request->buffer = malloc(length);
         if (!request->buffer)
-            return false;
+            return JNI_FALSE;
         memset(request->buffer, 0, length);
         if (out) {
             // copy data from Java buffer to native buffer
@@ -110,9 +110,9 @@
             request->buffer = NULL;
         }
         env->DeleteGlobalRef((jobject)request->client_data);
-        return false;
+        return JNI_FALSE;
     }
-    return true;
+    return JNI_TRUE;
 }
 
 static jint
@@ -141,13 +141,13 @@
     struct usb_request* request = get_request_from_object(env, thiz);
     if (!request) {
         ALOGE("request is closed in native_queue");
-        return false;
+        return JNI_FALSE;
     }
 
     if (buffer && length) {
         request->buffer = env->GetDirectBufferAddress(buffer);
         if (!request->buffer)
-            return false;
+            return JNI_FALSE;
     } else {
         request->buffer = NULL;
     }
@@ -161,9 +161,9 @@
     if (usb_request_queue(request)) {
         request->buffer = NULL;
         env->DeleteGlobalRef((jobject)request->client_data);
-        return false;
+        return JNI_FALSE;
     }
-    return true;
+    return JNI_TRUE;
 }
 
 static jint
@@ -185,7 +185,7 @@
     struct usb_request* request = get_request_from_object(env, thiz);
     if (!request) {
         ALOGE("request is closed in native_cancel");
-        return false;
+        return JNI_FALSE;
     }
     return (usb_request_cancel(request) == 0);
 }
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 8ba77ae..4b68c0d 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -23,9 +23,9 @@
 #include "selinux/selinux.h"
 #include "selinux/android.h"
 #include <errno.h>
+#include <memory>
 #include <ScopedLocalRef.h>
 #include <ScopedUtfChars.h>
-#include <UniquePtr.h>
 
 namespace android {
 
@@ -34,7 +34,7 @@
         freecon(p);
     }
 };
-typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
+typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext;
 
 static jboolean isSELinuxDisabled = true;
 
@@ -112,7 +112,7 @@
         return false;
     }
 
-    UniquePtr<ScopedUtfChars> context;
+    std::unique_ptr<ScopedUtfChars> context;
     const char* context_c_str = NULL;
     if (contextStr != NULL) {
         context.reset(new ScopedUtfChars(env, contextStr));
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 364ac44..7e01657 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -20,7 +20,6 @@
 #include "core_jni_helpers.h"
 
 #include <ScopedUtfChars.h>
-#include <UniquePtr.h>
 #include <androidfw/ZipFileRO.h>
 #include <androidfw/ZipUtils.h>
 #include <utils/Log.h>
@@ -37,6 +36,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <memory>
 
 #define APK_LIB "lib/"
 #define APK_LIB_LEN (sizeof(APK_LIB) - 1)
@@ -398,7 +398,7 @@
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
     if (it.get() == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
@@ -446,7 +446,7 @@
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
     if (it.get() == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index f619c39..af117d1 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -32,6 +32,7 @@
 #include <signal.h>
 #include <stdlib.h>
 #include <sys/capability.h>
+#include <sys/cdefs.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
@@ -685,11 +686,39 @@
 static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
     // Zygote process unmount root storage space initially before every child processes are forked.
     // Every forked child processes (include SystemServer) only mount their own root storage space
-    // And no need unmount storage operation in MountEmulatedStorage method.
-    // Zygote process does not utilize root storage spaces and unshared its mount namespace from the ART.
+    // and no need unmount storage operation in MountEmulatedStorage method.
+    // Zygote process does not utilize root storage spaces and unshares its mount namespace below.
+
+    // See storage config details at http://source.android.com/tech/storage/
+    // Create private mount namespace shared by all children
+    if (unshare(CLONE_NEWNS) == -1) {
+        RuntimeAbort(env, __LINE__, "Failed to unshare()");
+        return;
+    }
+
+    // Mark rootfs as being a slave so that changes from default
+    // namespace only flow into our children.
+    if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
+        RuntimeAbort(env, __LINE__, "Failed to mount() rootfs as MS_SLAVE");
+        return;
+    }
+
+    // Create a staging tmpfs that is shared by our children; they will
+    // bind mount storage into their respective private namespaces, which
+    // are isolated from each other.
+    const char* target_base = getenv("EMULATED_STORAGE_TARGET");
+    if (target_base != nullptr) {
+#define STRINGIFY_UID(x) __STRING(x)
+        if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
+                  "uid=0,gid=" STRINGIFY_UID(AID_SDCARD_R) ",mode=0751") == -1) {
+            ALOGE("Failed to mount tmpfs to %s", target_base);
+            RuntimeAbort(env, __LINE__, "Failed to mount tmpfs");
+            return;
+        }
+#undef STRINGIFY_UID
+    }
 
     UnmountTree("/storage");
-    return;
 }
 
 static const JNINativeMethod gMethods[] = {
diff --git a/core/res/res/anim-watch/progress_indeterminate_material.xml b/core/res/res/anim-watch/progress_indeterminate_material.xml
new file mode 100644
index 0000000..8f00d6c
--- /dev/null
+++ b/core/res/res/anim-watch/progress_indeterminate_material.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="3333"
+        android:interpolator="@interpolator/trim_start_interpolator"
+        android:propertyName="trimPathStart"
+        android:repeatCount="-1"
+        android:valueFrom="0"
+        android:valueTo="0.75"
+        android:valueType="floatType" />
+    <objectAnimator
+        android:duration="3333"
+        android:interpolator="@interpolator/trim_end_interpolator"
+        android:propertyName="trimPathEnd"
+        android:repeatCount="-1"
+        android:valueFrom="0"
+        android:valueTo="0.75"
+        android:valueType="floatType" />
+    <objectAnimator
+        android:duration="3333"
+        android:interpolator="@interpolator/trim_offset_interpolator"
+        android:propertyName="trimPathOffset"
+        android:repeatCount="-1"
+        android:valueFrom="0"
+        android:valueTo="0.25"
+        android:valueType="floatType" />
+</set>
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/anim-watch/progress_indeterminate_rotation_material.xml
similarity index 60%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to core/res/res/anim-watch/progress_indeterminate_rotation_material.xml
index 402a536..63e66ec 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/anim-watch/progress_indeterminate_rotation_material.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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,9 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@interpolator/progress_indeterminate_rotation_interpolator"
+    android:duration="16666"
+    android:propertyName="rotation"
+    android:repeatCount="-1"
+    android:valueFrom="0"
+    android:valueTo="720"
+    android:valueType="floatType" />
diff --git a/core/res/res/drawable-hdpi/watch_switch_track_mtrl.png b/core/res/res/drawable-hdpi/watch_switch_track_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/watch_switch_track_mtrl.png
rename to core/res/res/drawable-hdpi/watch_switch_track_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/watch_switch_track_mtrl.png b/core/res/res/drawable-xhdpi/watch_switch_track_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/watch_switch_track_mtrl.png
rename to core/res/res/drawable-xhdpi/watch_switch_track_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/watch_switch_track_mtrl.png b/core/res/res/drawable-xxhdpi/watch_switch_track_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/watch_switch_track_mtrl.png
rename to core/res/res/drawable-xxhdpi/watch_switch_track_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/drawable/watch_switch_track_material.xml
similarity index 73%
rename from core/res/res/color/watch_switch_track_color_material.xml
rename to core/res/res/drawable/watch_switch_track_material.xml
index 402a536..79e92a3 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/drawable/watch_switch_track_material.xml
@@ -1,21 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Copyright (C) 2016 The Android Open Source Project
-
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-
           http://www.apache.org/licenses/LICENSE-2.0
-
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
+
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
+    <item android:state_enabled="false">
+        <bitmap android:alpha="0.1" android:src="@drawable/watch_switch_track_mtrl_alpha" />
+    </item>
+    <item>
+        <bitmap android:alpha="0.2" android:src="@drawable/watch_switch_track_mtrl_alpha" />
+    </item>
 </selector>
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/interpolator-watch/progress_indeterminate_rotation_interpolator.xml
similarity index 60%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to core/res/res/interpolator-watch/progress_indeterminate_rotation_interpolator.xml
index 402a536..ed2655c 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/interpolator-watch/progress_indeterminate_rotation_interpolator.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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,9 +14,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.032,0.0 0.016,0.2 0.08,0.2 l 0.12,0.0 c 0.032,0.0 0.016,0.2 0.08,0.2 l 0.12,0.0 c 0.032,0.0 0.016,0.2 0.08,0.2 l 0.12,0.0 c 0.032,0.0 0.016,0.2 0.08,0.2 l 0.12,0.0 c 0.032,0.0 0.016,0.2 0.08,0.2 l 0.12,0.0 L 1.0,1.0" />
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/interpolator-watch/trim_end_interpolator.xml
similarity index 61%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to core/res/res/interpolator-watch/trim_end_interpolator.xml
index 402a536..f46d5e0 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/interpolator-watch/trim_end_interpolator.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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,9 +14,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.08,0.0 0.04,1.0 0.2,1.0 l 0.8,0.0 L 1.0,1.0" />
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/interpolator-watch/trim_offset_interpolator.xml
similarity index 61%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to core/res/res/interpolator-watch/trim_offset_interpolator.xml
index 402a536..d58672e 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/interpolator-watch/trim_offset_interpolator.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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,9 +14,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 0.4,1.0 l 0.6,0.0 L 1.0,1.0" />
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/interpolator-watch/trim_start_interpolator.xml
similarity index 61%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to core/res/res/interpolator-watch/trim_start_interpolator.xml
index 402a536..365609c 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/interpolator-watch/trim_start_interpolator.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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,9 +14,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 0.2,0.0 c 0.08,0.0 0.04,1.0 0.2,1.0 l 0.6,0.0 L 1.0,1.0" />
diff --git a/core/res/res/layout-watch/alert_dialog_material.xml b/core/res/res/layout-watch/alert_dialog_material.xml
index 002dde8..2fe13de 100644
--- a/core/res/res/layout-watch/alert_dialog_material.xml
+++ b/core/res/res/layout-watch/alert_dialog_material.xml
@@ -45,7 +45,7 @@
             <!-- Content Panel -->
             <FrameLayout android:id="@+id/contentPanel"
                     android:layout_width="match_parent"
-                    android:layout_height="match_parent"
+                    android:layout_height="wrap_content"
                     android:clipToPadding="false">
                 <TextView android:id="@+id/message"
                         android:layout_width="match_parent"
diff --git a/core/res/res/layout-watch/preference_widget_switch.xml b/core/res/res/layout-watch/preference_widget_switch.xml
index a1a845a..5881cf0 100644
--- a/core/res/res/layout-watch/preference_widget_switch.xml
+++ b/core/res/res/layout-watch/preference_widget_switch.xml
@@ -24,8 +24,7 @@
     android:thumb="@drawable/watch_switch_thumb_material_anim"
     android:thumbTint="@color/watch_switch_thumb_color_material"
     android:thumbTintMode="multiply"
-    android:track="@drawable/watch_switch_track_mtrl"
-    android:trackTint="@color/watch_switch_track_color_material"
+    android:track="@drawable/watch_switch_track_material"
     android:focusable="false"
     android:clickable="false"
     android:background="@null" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c0afffd..ff35ce9 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Gekoppel aan <xliff:g id="SESSION">%s</xliff:g>. Tik om die netwerk te bestuur."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Altydaan-VPN koppel tans..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altydaan-VPN gekoppel"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Altydaan-VPN is ontkoppel"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Altydaan-VPN-fout"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tik om op te stel"</string>
     <string name="upload_file" msgid="2897957172366730416">"Kies lêer"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Geen lêer gekies nie"</string>
     <string name="reset" msgid="2448168080964209908">"Stel terug"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Voer taalnaam in"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Voorgestel"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Alle tale"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Soek"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Werkmodus is AF"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Stel werkprofiel in staat om te werk, insluitend programme, agtergrondsinkronisering en verwante kenmerke."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 206fe0a..6e4066d 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"ለ<xliff:g id="SESSION">%s</xliff:g> የተገናኘ። አውታረመረቡን ለማደራጀት ሁለቴ ንካ።"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ሁልጊዜ የበራ VPN በመገናኘት ላይ…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ሁልጊዜ የበራ VPN ተገናኝቷል"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"ሁልጊዜ የበራ የVPN ግንኙነት ተቋርጧል"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"ሁልጊዜ የበራ VPN ስህተት"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"ለማዋቀር መታ ያድርጉ"</string>
     <string name="upload_file" msgid="2897957172366730416">"ፋይል ምረጥ"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ምንም ፋይል አልተመረጠም"</string>
     <string name="reset" msgid="2448168080964209908">"ዳግም አስጀምር"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"የቋንቋ ስም ይተይቡ"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"የተጠቆሙ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ሁሉም ቋንቋዎች"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"ፈልግ"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"የሥራ ሁነታ ጠፍቷል"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"መተግበሪያዎችን፣ የበስተጀርባ ሥምረት እና ተዛማጅ ባሕሪዎችን ጨምሮ የሥራ መገለጫ እንዲሰራ ይፍቀዱ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1541789..b3364d4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1343,11 +1343,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"تم الاتصال بـ <xliff:g id="SESSION">%s</xliff:g>. انقر لإدارة الشبكة."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"‏جارٍ الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"‏تم الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"‏تم فصل الشبكة الظاهرية الخاصة (VPN) دائمة التشغيل"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"‏خطأ بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"انقر للإعداد."</string>
     <string name="upload_file" msgid="2897957172366730416">"اختيار ملف"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"لم يتم اختيار أي ملف"</string>
     <string name="reset" msgid="2448168080964209908">"إعادة تعيين"</string>
@@ -1794,6 +1792,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"اكتب اسم اللغة"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"المقترحة"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"جميع اللغات"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"كل المناطق"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"البحث"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"وضع العمل معطَّل"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"السماح باستخدام الملف الشخصي للعمل، بما في ذلك التطبيقات ومزامنة الخلفية والميزات ذات الصلة."</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 9845b69..672a208 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> sessiyaya qoşulun. Şəbəkəni idarə etmək üçün tıklayın."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Həmişə aktiv VPN bağlanır..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN bağlantısı həmişə aktiv"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Həmişə aktiv VPN bağlantısı kəsildi"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Həmişə aktiv VPN xətası"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Quraşdırmaq üçün tıklayın"</string>
     <string name="upload_file" msgid="2897957172366730416">"Fayl seçin"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Heç bir fayl seçilməyib"</string>
     <string name="reset" msgid="2448168080964209908">"Sıfırlayın"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Dil adını daxil edin"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Təklif edilmiş"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Bütün dillər"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Bütün bölgələr"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Axtarın"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"İş rejimi DEAKTİVDİR"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Tətbiq, arxa fon sinxronizasiyası və digər əlaqədar xüsusiyyətlər daxil olmaqla iş profilinin fəaliyyətinə icazə verin."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e8d84d5..16851a6 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1268,11 +1268,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Povezano sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite da biste upravljali mrežom."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje stalno uključenog VPN-a..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Stalno uključeni VPN je povezan"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Veza sa stalno uključenim VPN-om je prekinuta"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Greška stalno uključenog VPN-a"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Dodirnite da biste podesili"</string>
     <string name="upload_file" msgid="2897957172366730416">"Odaberi datoteku"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nije izabrana nijedna datoteka"</string>
     <string name="reset" msgid="2448168080964209908">"Ponovo postavi"</string>
@@ -1686,6 +1684,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Unesite naziv jezika"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Predloženi"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Svi jezici"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Pretraži"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Režim za Work je ISKLJUČEN"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Dozvoljava profilu za Work da funkcioniše, uključujući aplikacije, sinhronizaciju u pozadini i srodne funkcije."</string>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 792726a..2cfca40 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Падлучаны да <xliff:g id="SESSION">%s</xliff:g>. Націсніце, каб кiраваць сеткай."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Падключэнне заўсёды ўключанага VPN..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Заўсёды ўключаны i падключаны VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Заўсёды ўключаны VPN адключаны"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Памылка заўсёды ўключанага VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Дакраніцеся, каб наладзіць"</string>
     <string name="upload_file" msgid="2897957172366730416">"Выберыце файл"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Файл не выбраны"</string>
     <string name="reset" msgid="2448168080964209908">"Скінуць"</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Увядзіце назву мовы"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Прапанаваныя"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Усе мовы"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Усе рэгіёны"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Шукаць"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Рэжым працы АДКЛЮЧАНЫ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Дазволіць функцыянаванне працоўнага профілю, у тым ліку праграм, фонавай сінхранізацыі і звязаных з імі функцый."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 38f5b93..15538ce 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Свързана с/ъс <xliff:g id="SESSION">%s</xliff:g>. Докоснете, за да управлявате мрежата."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Установява се връзка с винаги включената виртуална частна мрежа (VPN)…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Установена е връзка с винаги включената виртуална частна мрежа (VPN)"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Няма връзка с винаги включената виртуална частна мрежа (VPN)"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка във винаги включената виртуална частна мрежа (VPN)"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Докоснете, за да настроите"</string>
     <string name="upload_file" msgid="2897957172366730416">"Избор на файл"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Няма избран файл"</string>
     <string name="reset" msgid="2448168080964209908">"Повторно задаване"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Въведете име на език"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Предложени"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Всички езици"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Всички региони"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Търсене"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Работният режим е ИЗКЛЮЧЕН"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Разрешаване на функционирането на служебния потребителски профил, включително приложенията, синхронизирането на заден план и свързаните функции."</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 8dc65fd..27287e4 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> তে সংযুক্ত হয়েছে৷ নেটওয়ার্ক পরিচালনা করতে আলতো চাপুন৷"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"সর্বদা-চালু VPN সংযুক্ত হচ্ছে..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"সর্বদা-চালু VPN সংযুক্ত হয়েছে"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"সর্বদা-চালু VPN এর সংযোগ বিচ্ছিন্ন হয়েছে"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"সর্বদা-চালু VPN ত্রুটি"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"সেট আপ করতে আলতো চাপুন"</string>
     <string name="upload_file" msgid="2897957172366730416">"ফাইল বেছে নিন"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"কোনো ফাইল নির্বাচন করা হয়নি"</string>
     <string name="reset" msgid="2448168080964209908">"পুনরায় সেট করুন"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ভাষার নাম লিখুন"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"প্রস্তাবিত"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"সকল ভাষা"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"সমস্ত অঞ্চল"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"অনুসন্ধান করুন"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"কাজের মোড বন্ধ আছে"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"অ্যাপ্লিকেশান, পটভূমি সিঙ্ক এবং সম্পর্কিত বৈশিষ্ট্যগুলি সহ কর্মস্থলের প্রোফাইলটিকে কাজ করার অনুমতি দিন।"</string>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 7ea04e0..66e0791 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1270,11 +1270,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Povezano sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite da upravljate mrežom."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje na uvijek aktivni VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Povezan na uvijek aktivni VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Uvijek aktivni VPN nije povezan"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Greška u povezivanju na uvijek aktivni VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Dodirnite za postavke"</string>
     <string name="upload_file" msgid="2897957172366730416">"Odabir fajla"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nije izabran nijedan fajl"</string>
     <string name="reset" msgid="2448168080964209908">"Ponovno pokretanje"</string>
@@ -1688,6 +1686,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Ukucajte naziv jezika"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Predloženo"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Svi jezici"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Sve regije"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Pretraga"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Radni način rada je ISKLJUČEN"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Omogući radnom profilu da funkcionira, uključujući aplikacije, sinhronizaciju u pozadini i povezane funkcije."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index b48ba58..8f0d9fa 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Connectat a <xliff:g id="SESSION">%s</xliff:g>. Pica per gestionar la xarxa."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"T\'estàs connectant a la VPN sempre activada…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Estàs connectat a la VPN sempre activada"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"La VPN sempre activada està desconnectada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error de la VPN sempre activada"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Toca per configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Trieu un fitxer"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"No s\'ha escollit cap fitxer"</string>
     <string name="reset" msgid="2448168080964209908">"Restableix"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Nom de l\'idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suggerits"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Tots els idiomes"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Totes les regions"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Cerca"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Mode de feina desactivat"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permet que el perfil professional funcioni, incloses les aplicacions, la sincronització en segon pla i les funcions relacionades."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4c24f23..439852a 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Připojeno k relaci <xliff:g id="SESSION">%s</xliff:g>. Klepnutím můžete síť spravovat."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Připojování k trvalé síti VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Je připojena trvalá síť VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Trvalá síť VPN je odpojena"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba trvalé sítě VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Klepnutím přejděte do Nastavení"</string>
     <string name="upload_file" msgid="2897957172366730416">"Zvolit soubor"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Není vybrán žádný soubor"</string>
     <string name="reset" msgid="2448168080964209908">"Resetovat"</string>
@@ -1722,6 +1720,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Zadejte název jazyka"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Navrhované"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Všechny jazyky"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Vyhledávání"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Pracovní režim je VYPNUTÝ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Povolí fungování pracovního profilu, včetně aplikací, synchronizace na pozadí a souvisejících funkcí."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index d391cc3..5d1beba 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Forbundet til <xliff:g id="SESSION">%s</xliff:g>. Tryk for at administrere netværket."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til altid aktiveret VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN er forbundet"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til altid aktiveret VPN er afbrudt"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i altid aktiveret VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tryk for at konfigurere"</string>
     <string name="upload_file" msgid="2897957172366730416">"Vælg fil"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
     <string name="reset" msgid="2448168080964209908">"Nulstil"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Angiv sprogets navn"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Foreslået"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Alle sprog"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Alle områder"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Søg"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Arbejdstilstand er slået FRA"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Tillad, at arbejdsprofilen aktiveres, bl.a. i forbindelse med apps, baggrundssynkronisering og relaterede funktioner."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2f5e30d..407a397 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Verbunden mit <xliff:g id="SESSION">%s</xliff:g>. Zum Verwalten des Netzwerks tippen"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Verbindung zu durchgehend aktivem VPN wird hergestellt…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Mit durchgehend aktivem VPN verbunden"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Verbindung zu durchgehend aktivem VPN getrennt"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Durchgehend aktives VPN – Verbindungsfehler"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Zum Einrichten tippen"</string>
     <string name="upload_file" msgid="2897957172366730416">"Datei auswählen"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Keine ausgewählt"</string>
     <string name="reset" msgid="2448168080964209908">"Zurücksetzen"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Sprache eingeben"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Vorschläge"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Alle Sprachen"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Alle Regionen"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Suche"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Arbeitsmodus ist AUS"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Arbeitsprofil aktivieren, einschließlich Apps, Synchronisierung im Hintergrund und verknüpfter Funktionen."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 34acbee..2a766e6 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Συνδέθηκε με <xliff:g id="SESSION">%s</xliff:g>. Πατήστε για να διαχειριστείτε το δίκτυο."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Σύνδεση πάντα ενεργοποιημένου VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Έχει συνδεθεί πάντα ενεργοποιημένο VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Το πάντα ενεργοποιημένο VPN αποσυνδέθηκε"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Σφάλμα πάντα ενεργοποιημένου VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Πατήστε για ρύθμιση"</string>
     <string name="upload_file" msgid="2897957172366730416">"Επιλογή αρχείου"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Δεν επιλέχθηκε κανένα αρχείο."</string>
     <string name="reset" msgid="2448168080964209908">"Επαναφορά"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Εισαγ. όνομα γλώσσας"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Προτεινόμενες"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Όλες οι γλώσσες"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Όλες οι περιοχές"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Αναζήτηση"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Λειτουργία εργασίας ΑΠΕΝΕΡΓ/ΝΗ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Να επιτρέπεται η λειτουργία του προφίλ εργασίας σας, συμπεριλαμβανομένων των εφαρμογών, του συγχρονισμού στο παρασκήνιο και των σχετικών λειτουργιών."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 27a3ecb..1fe415e 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Connected to <xliff:g id="SESSION">%s</xliff:g>. Tap to manage the network."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Always-on VPN connecting…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN connected"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Always-on VPN disconnected"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Always-on VPN error"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tap to set up"</string>
     <string name="upload_file" msgid="2897957172366730416">"Choose file"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"No file chosen"</string>
     <string name="reset" msgid="2448168080964209908">"Reset"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suggested"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"All languages"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"All regions"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Search"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Work mode is OFF"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Allow work profile to function, including apps, background sync and related features."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 27a3ecb..1fe415e 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Connected to <xliff:g id="SESSION">%s</xliff:g>. Tap to manage the network."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Always-on VPN connecting…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN connected"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Always-on VPN disconnected"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Always-on VPN error"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tap to set up"</string>
     <string name="upload_file" msgid="2897957172366730416">"Choose file"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"No file chosen"</string>
     <string name="reset" msgid="2448168080964209908">"Reset"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suggested"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"All languages"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"All regions"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Search"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Work mode is OFF"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Allow work profile to function, including apps, background sync and related features."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 27a3ecb..1fe415e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Connected to <xliff:g id="SESSION">%s</xliff:g>. Tap to manage the network."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Always-on VPN connecting…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN connected"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Always-on VPN disconnected"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Always-on VPN error"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tap to set up"</string>
     <string name="upload_file" msgid="2897957172366730416">"Choose file"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"No file chosen"</string>
     <string name="reset" msgid="2448168080964209908">"Reset"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suggested"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"All languages"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"All regions"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Search"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Work mode is OFF"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Allow work profile to function, including apps, background sync and related features."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 85ef77c..dbefd6c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Pulsa para gestionar la red."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Estableciendo conexión con la VPN siempre activada..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Se estableció conexión con la VPN siempre activada."</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Se desconectó la VPN siempre activada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Se produjo un error al establecer conexión con la VPN siempre activada."</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Presiona para configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Elegir archivo"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"No se seleccionó un archivo."</string>
     <string name="reset" msgid="2448168080964209908">"Restablecer"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Nombre del idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugeridos"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Todos los idiomas"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Búsqueda"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modo de trabajo DESACTIVADO"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permite que se active el perfil de trabajo, incluidas las apps, la sincronización en segundo plano y las funciones relacionadas."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 94b1961..c7d3881 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Conectando VPN siempre activada…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN siempre activada conectada"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN siempre activada desconectada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error de VPN siempre activada"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Toca para configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Seleccionar archivo"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Archivo no seleccionado"</string>
     <string name="reset" msgid="2448168080964209908">"Restablecer"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Nombre de idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugeridos"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Todos los idiomas"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Todas las regiones"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Buscar"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modo de trabajo desactivado"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permite que se utilice el perfil de trabajo, incluidas las aplicaciones, la sincronización en segundo plano y las funciones relacionadas."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index e7b7ab0..cd751a8a 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Ühendatud seansiga <xliff:g id="SESSION">%s</xliff:g>. Koputage võrgu haldamiseks"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ühendamine alati sees VPN-iga …"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ühendatud alati sees VPN-iga"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Alati sees VPN pole ühendatud"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Alati sees VPN-i viga"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Puudutage seadistamiseks"</string>
     <string name="upload_file" msgid="2897957172366730416">"Valige fail"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ühtegi faili pole valitud"</string>
     <string name="reset" msgid="2448168080964209908">"Lähtesta"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Sisestage keele nimi"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Soovitatud"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Kõik keeled"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Kõik piirkonnad"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Otsing"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Töörežiim on VÄLJA LÜLITATUD"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Lubatakse tööprofiili toimingud, sh rakendused, taustal sünkroonimine ja seotud funktsioonid."</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 7727635..f84dde3 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> saiora konektatuta. Sakatu sarea kudeatzeko."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Beti aktibatuta dagoen VPNa konektatzen…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Beti aktibatuta dagoen VPNa konektatu da"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Deskonektatu egin da beti aktibatuta dagoen VPN konexioa"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Beti aktibatuta dagoen VPN errorea"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Konfiguratzeko, sakatu hau"</string>
     <string name="upload_file" msgid="2897957172366730416">"Aukeratu fitxategia"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ez da fitxategirik aukeratu"</string>
     <string name="reset" msgid="2448168080964209908">"Berrezarri"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Adierazi hizkuntza"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Iradokitakoak"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Hizkuntza guztiak"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Bilaketa"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Desaktibatuta dago laneko modua"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Baimendu laneko profilak funtzionatzea, besteak beste, aplikazioak, atzeko planoko sinkronizazioa eta erlazionatutako eginbideak."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 913bd73..140b599 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -924,9 +924,9 @@
       <item quantity="one">در <xliff:g id="COUNT_1">%d</xliff:g> سال</item>
       <item quantity="other">در <xliff:g id="COUNT_1">%d</xliff:g> سال</item>
     </plurals>
-    <string name="VideoView_error_title" msgid="3534509135438353077">"مشکل در ویدیو"</string>
-    <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"متأسفیم، این ویدیو برای پخش جریانی با این دستگاه معتبر نیست."</string>
-    <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"پخش این ویدیو ممکن نیست."</string>
+    <string name="VideoView_error_title" msgid="3534509135438353077">"مشکل در ویدئو"</string>
+    <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"متأسفیم، این ویدئو برای پخش جریانی با این دستگاه معتبر نیست."</string>
+    <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"پخش این ویدئو ممکن نیست."</string>
     <string name="VideoView_error_button" msgid="2822238215100679592">"تأیید"</string>
     <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>، <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="noon" msgid="7245353528818587908">"ظهر"</string>
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"به <xliff:g id="SESSION">%s</xliff:g> متصل شد. برای مدیریت شبکه ضربه بزنید."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"‏در حال اتصال VPN همیشه فعال…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"‏VPN همیشه فعال متصل شد"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"‏ارتباط VPN همیشه روشن قطع شد"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"‏خطای VPN همیشه فعال"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"برای راه‌اندازی ضربه بزنید"</string>
     <string name="upload_file" msgid="2897957172366730416">"انتخاب فایل"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"هیچ فایلی انتخاب نشد"</string>
     <string name="reset" msgid="2448168080964209908">"بازنشانی"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"نام زبان را تایپ کنید"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"پیشنهادشده"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"همه زبان‌ها"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"همه منطقه‌ها"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"جستجو"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"حالت کاری خاموش است"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"به نمایه کاری اجازه فعالیت ( شامل استفاده از برنامه‌ها، همگام‌سازی در پس‌زمینه و قابلیت‌های مرتبط) داده شود."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a515724..9676cc2 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Yhdistetty: <xliff:g id="SESSION">%s</xliff:g>. Hallinnoi verkkoa napauttamalla."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Yhdistetään aina käytössä olevaan VPN-verkkoon..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Yhdistetty aina käytössä olevaan VPN-verkkoon"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Aina käytössä olevan VPN:n yhteys on katkaistu"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Aina käytössä oleva VPN: virhe"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Määritä koskettamalla."</string>
     <string name="upload_file" msgid="2897957172366730416">"Valitse tiedosto"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ei valittua tiedostoa"</string>
     <string name="reset" msgid="2448168080964209908">"Palauta"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Anna kielen nimi"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Ehdotukset"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Kaikki kielet"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Kaikki alueet"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Haku"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Työtila on pois käytöstä"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Sallii työprofiiliin toiminnan, esimerkiksi sovellukset ja taustasynkronoinnin."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 09162b2..f071ee2 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Connecté à <xliff:g id="SESSION">%s</xliff:g>. Appuyez ici pour gérer le réseau."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN permanent en cours de connexion…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN permanent connecté"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"RPV permanent déconnecté"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erreur du VPN permanent"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Touchez pour configurer"</string>
     <string name="upload_file" msgid="2897957172366730416">"Choisir un fichier"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Aucun fichier sélectionné"</string>
     <string name="reset" msgid="2448168080964209908">"Réinitialiser"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Entrez la langue"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suggestions"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Toutes les langues"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Rechercher"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Le mode Travail est désactivé"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Autoriser le fonctionnement du profil professionnel, y compris les applications, la synchronisation en arrière-plan et les fonctionnalités associées."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index af4ec3c..df6024f5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Connecté à <xliff:g id="SESSION">%s</xliff:g>. Appuyez ici pour gérer le réseau."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN permanent en cours de connexion…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN permanent connecté"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN permanent déconnecté"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erreur du VPN permanent"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Appuyer pour configurer"</string>
     <string name="upload_file" msgid="2897957172366730416">"Sélectionner un fichier"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Aucun fichier sélectionné"</string>
     <string name="reset" msgid="2448168080964209908">"Réinitialiser"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Saisissez la langue"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Recommandations"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Toutes les langues"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Rechercher"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Mode professionnel DÉSACTIVÉ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Autoriser le fonctionnement du profil professionnel, y compris les applications, la synchronisation en arrière-plan et les fonctionnalités associées."</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index c676a09..42764bd 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca aquí para xestionar a rede."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sempre activada conectándose..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre activada conectada"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Desconectouse a VPN sempre activada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro na VPN sempre activada"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tocar para configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Escoller un ficheiro"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Non se seleccionou ningún ficheiro"</string>
     <string name="reset" msgid="2448168080964209908">"Restablecer"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Nome do idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suxeridos"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Todos os idiomas"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Buscar"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modo de traballo DESACTIVADO"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permite que funcione o perfil de traballo, incluídas as aplicacións, a sincronización en segundo plano e as funcións relacionadas."</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 482d115..632cfae 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> થી કનેક્ટ થયાં. નેટવર્કને સંચાલિત કરવા માટે ટૅપ કરો."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"હંમેશા-ચાલુ VPN કનેક્ટ થઈ રહ્યું છે…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"હંમેશા-ચાલુ VPN કનેક્ટ થયું"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"હંમેશાં-ચાલુ VPN ડિસ્કનેક્ટ થયું"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"હંમેશાં ચાલુ VPN ભૂલ"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"સેટ કરવા માટે ટૅપ કરો"</string>
     <string name="upload_file" msgid="2897957172366730416">"ફાઇલ પસંદ કરો"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"કોઈ ફાઇલ પસંદ કરેલી નથી"</string>
     <string name="reset" msgid="2448168080964209908">"ફરીથી સેટ કરો"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ભાષાનું નામ ટાઇપ કરો"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"સૂચવેલા"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"બધી ભાષાઓ"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"તમામ પ્રદેશ"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"શોધ"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"કાર્ય મોડ બંધ છે"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"કાર્ય પ્રોફાઇલને ઍપ્લિકેશનો, પૃષ્ઠભૂમિ સમન્વયન અને સંબંધિત સુવિધાઓ સહિતનું કાર્ય કરવાની મંજૂરી આપો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 239e7df..896565e 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> से कनेक्‍ट किया गया. नेटवर्क प्रबंधित करने के लिए टैप करें."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"हमेशा-चालू VPN कनेक्ट हो रहा है…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"हमेशा-चालू VPN कनेक्ट है"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"हमेशा-चालू VPN डिस्‍कनेक्‍ट है"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"हमेशा-चालू VPN त्रुटि"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"सेट करने के लिए टैप करें"</string>
     <string name="upload_file" msgid="2897957172366730416">"फ़ाइल चुनें"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"कोई फ़ाइल चुनी नहीं गई"</string>
     <string name="reset" msgid="2448168080964209908">"रीसेट करें"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"भाषा का नाम लिखें"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"सुझाए गए"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"सभी भाषाएं"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"सभी क्षेत्र"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"खोजें"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"कार्य मोड बंद है"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"ऐप्स, पृष्ठभूमि समन्वयन और संबंधित सुविधाओं सहित कार्य प्रोफ़ाइल को काम करने की अनुमति दें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 8699115..5934956 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1268,11 +1268,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Povezan sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dotaknite za upravljanje mrežom."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje s uvijek uključenom VPN mrežom…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Povezan s uvijek uključenom VPN mrežom"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Prekinuta je veza s uvijek uključenom VPN mrežom"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Pogreška uvijek uključene VPN mreže"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Dodirnite za postavljanje"</string>
     <string name="upload_file" msgid="2897957172366730416">"Odaberite datoteku"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nema odabranih datoteka"</string>
     <string name="reset" msgid="2448168080964209908">"Ponovo postavi"</string>
@@ -1686,6 +1684,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Unesite naziv jezika"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Predloženo"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Svi jezici"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Sve regije"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Pretraži"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Radni je način ISKLJUČEN"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Omogućuje radnom profilu da funkcionira, uključujući aplikacije, sinkronizaciju u pozadini i povezane značajke."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3557fd5..75a7760 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Csatlakozva ide: <xliff:g id="SESSION">%s</xliff:g>. Érintse meg a hálózat kezeléséhez."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Csatlakozás a mindig bekapcsolt VPN-hez..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Csatlakozva a mindig bekapcsolt VPN-hez"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Kapcsolat bontva a mindig bekapcsolt VPN-nel"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Hiba a mindig bekapcsolt VPN-nel"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Koppintson ide a beállításhoz"</string>
     <string name="upload_file" msgid="2897957172366730416">"Fájl kiválasztása"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nincs fájl kiválasztva"</string>
     <string name="reset" msgid="2448168080964209908">"Alaphelyzet"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Adja meg a nyelvet"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Javasolt"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Minden nyelv"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Minden régió"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Keresés"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"A munka mód KI van kapcsolva"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Munkaprofil használatának engedélyezése, beleértve az alkalmazásokat, a háttérben való szinkronizálást és a kapcsolódó funkciókat."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index b584b6c..edd5226 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Կապակացված է <xliff:g id="SESSION">%s</xliff:g>-ին: Սեղմեք` ցանցը կառավարելու համար:"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Միշտ-միացված VPN-ը կապվում է..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Միշտ-առցանց VPN-ը կապակցված է"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"«Միշտ միացված VPN»-ն անջատված է"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"VPN սխալը միշտ միացված"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Հպեք՝ կարգավորելու համար"</string>
     <string name="upload_file" msgid="2897957172366730416">"Ընտրել ֆայլը"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ոչ մի ֆայլ չի ընտրված"</string>
     <string name="reset" msgid="2448168080964209908">"Վերակայել"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Մուտքագրեք լեզուն"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Առաջարկներ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Բոլոր լեզուները"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Բոլոր տարածաշրջանները"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Որոնում"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Աշխատանքային ռեժիմն ԱՆՋԱՏՎԱԾ Է"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Միացնել աշխատանքային պրոֆիլը՝ հավելվածները, ֆոնային համաժամեցումը և առնչվող գործառույթները"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 300d122..0715686 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Tersambung ke <xliff:g id="SESSION">%s</xliff:g>. Ketuk untuk mengelola jaringan."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Menyambungkan VPN selalu aktif..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN selalu aktif tersambung"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN selalu aktif terputus"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kesalahan VPN selalu aktif"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Ketuk untuk menyiapkan"</string>
     <string name="upload_file" msgid="2897957172366730416">"Pilih file"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Tidak ada file yang dipilih"</string>
     <string name="reset" msgid="2448168080964209908">"Setel ulang"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Ketik nama bahasa"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Disarankan"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Semua bahasa"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Semua wilayah"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Telusuri"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Mode kerja NONAKTIF"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Izinkan profil kerja berfungsi, termasuk aplikasi, sinkronisasi latar belakang, dan fitur terkait."</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index c7ea619..89104ec 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Tengt við <xliff:g id="SESSION">%s</xliff:g>. Ýttu til að hafa umsjón með netinu."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Sívirkt VPN tengist…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Sívirkt VPN tengt"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Sívirkt VPN aftengt"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Villa í sívirku VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Ýttu til að setja upp"</string>
     <string name="upload_file" msgid="2897957172366730416">"Velja skrá"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Engin skrá valin"</string>
     <string name="reset" msgid="2448168080964209908">"Endurstilla"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Sláðu inn heiti tungumáls"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Tillögur"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Öll tungumál"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Öll svæði"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Leita"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Slökkt á vinnusniði"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Leyfa virkni vinnusniðs, m.a. forrita, samstillingar í bakgrunni og tengdra eiginleika."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 83f91ea..b132b2f 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Collegata a <xliff:g id="SESSION">%s</xliff:g>. Tocca per gestire la rete."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Connessione a VPN sempre attiva…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre attiva connessa"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN sempre attiva disconnessa"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Errore VPN sempre attiva"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tocca per configurare"</string>
     <string name="upload_file" msgid="2897957172366730416">"Scegli file"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nessun file è stato scelto"</string>
     <string name="reset" msgid="2448168080964209908">"Reimposta"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Digita nome lingua"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Suggerite"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Tutte le lingue"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Tutte le aree geografiche"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Cerca"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modalità Lavoro DISATTIVATA"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Attiva il profilo di lavoro, incluse app, sincronizzazione in background e funzioni correlate."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index df65070..aa68af1a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"מחובר אל <xliff:g id="SESSION">%s</xliff:g>. הקש כדי לנהל את הרשת."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"‏ה-VPN שמופעל תמיד, מתחבר..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"‏ה-VPN שפועל תמיד, מחובר"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"‏חיבור תמידי ל-VPN מנותק"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"‏שגיאת VPN שמופעל תמיד"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"הקש כדי להגדיר"</string>
     <string name="upload_file" msgid="2897957172366730416">"בחר קובץ"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"לא נבחר קובץ"</string>
     <string name="reset" msgid="2448168080964209908">"איפוס"</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"הקלד שם שפה"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"הצעות"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"כל השפות"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"כל האזורים"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"חיפוש"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"מצב העבודה כבוי"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"אפשר לפרופיל העבודה לפעול, כולל אפליקציות, סנכרון ברקע ותכונות קשורות."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f331a4e..0b682bc 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>に接続しました。ネットワークを管理するにはタップしてください。"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPNに常時接続しています…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPNに常時接続しました"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"常時接続 VPN の接続を解除しました"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"常時接続VPNのエラー"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"設定するにはタップします"</string>
     <string name="upload_file" msgid="2897957172366730416">"ファイルを選択"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ファイルが選択されていません"</string>
     <string name="reset" msgid="2448168080964209908">"リセット"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"言語名を入力"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"言語の候補"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"すべての言語"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"すべての地域"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"検索"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Work モード OFF"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"仕事用プロファイルで、アプリ、バックグラウンド同期などの関連機能の使用を許可します。"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index d9dffef..28574b0 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"მიერთებულია <xliff:g id="SESSION">%s</xliff:g>-ზე. შეეხეთ ქსელის სამართავად."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"მიმდინარეობს მუდმივად ჩართული VPN-ის მიერთება…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"მუდმივად ჩართული VPN-ის მიერთებულია"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"მუდმივად ჩართული VPN გათიშულია"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"შეცდომა მუდამ VPN-ზე"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"შეეხეთ დასაყენებლად"</string>
     <string name="upload_file" msgid="2897957172366730416">"ფაილის არჩევა"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ფაილი არჩეული არ არის"</string>
     <string name="reset" msgid="2448168080964209908">"საწყისზე დაბრუნება"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"აკრიფეთ ენის სახელი"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"რეკომენდებული"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ყველა ენა"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"ყველა რეგიონი"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"ძიება"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"სამსახურის რეჟიმი გამორთულია"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"სამსახურის პროფილის მუშაობის დაშვება, მათ შორის, აპების, ფონური სინქრონიზაციის და დაკავშირებული ფუნქციების."</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 3f72e2a..1827c1c 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> жүйесіне жалғанған. Желіні басқару үшін түріңіз."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Әрқашан қосылған ВЖЖ жалғануда…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Әрқашан қосылған ВЖЖ жалғанған"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Әрқашан қосулы VPN желісі ажыратылды"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Әрқашан қосылған ВЖЖ қателігі"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Реттеу үшін түртіңіз"</string>
     <string name="upload_file" msgid="2897957172366730416">"Файлды таңдау"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ешқандай файл таңдалмаған"</string>
     <string name="reset" msgid="2448168080964209908">"Қайта реттеу"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Тіл атауын теріңіз"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Ұсынылған"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Барлық тілдер"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Барлық аймақтар"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Іздеу"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Жұмыс режимі ӨШІРУЛІ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Жұмыс профиліне, соның ішінде, қолданбаларға, фондық синхрондауға және қатысты мүмкіндіктерге жұмыс істеуге рұқсат ету."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 3dafa2a..6108973 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1245,11 +1245,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"បាន​ភ្ជាប់​ទៅ <xliff:g id="SESSION">%s</xliff:g> ។ ប៉ះ ដើម្បី​គ្រប់គ្រង​បណ្ដាញ។"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"បើក​ការ​តភ្ជាប់ VPN ជា​និច្ច..។"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ភ្ជាប់ VPN ជា​និច្ច"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"បានផ្តាច់ VPN ដែលបើកជានិច្ច"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"បើក​កំហុស VPN ជា​និច្ច"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"ប៉ះដើម្បីដំឡើង"</string>
     <string name="upload_file" msgid="2897957172366730416">"ជ្រើស​​ឯកសារ"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"គ្មាន​ឯកសារ​បាន​ជ្រើស"</string>
     <string name="reset" msgid="2448168080964209908">"កំណត់​ឡើងវិញ"</string>
@@ -1652,6 +1650,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"វាយបញ្ចូលឈ្មោះភាសា"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"បាន​ស្នើ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ភាសាទាំងអស់"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"ស្វែងរក"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"របៀបការងារបានបិទ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"អនុញ្ញាតឲ្យប្រវត្តិរូបការងារដំណើរការ ដោយរាប់បញ្ចូលទាំងកម្មវិធី ការធ្វើសមកាលកម្មផ្ទៃខាងក្រោយ និងលក្ខណៈពិសេសដែលពាក់ព័ន្ធ។"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 545e42f..3868529 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ. ನೆಟ್‍ವರ್ಕ್ ನಿರ್ವಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ಯಾವಾಗಲೂ-ಆನ್ VPN ಸಂಪರ್ಕಗೊಳ್ಳುತ್ತಿದೆ…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ಯಾವಾಗಲೂ-ಆನ್ VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"ಯಾವಾಗಲೂ-ಆನ್ VPN ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"ಯಾವಾಗಲೂ-ಆನ್ VPN ದೋಷ"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="upload_file" msgid="2897957172366730416">"ಫೈಲ್ ಆಯ್ಕೆಮಾಡು"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ಯಾವುದೇ ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿಲ್ಲ"</string>
     <string name="reset" msgid="2448168080964209908">"ಮರುಹೊಂದಿಸು"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ಭಾಷೆ ಹೆಸರನ್ನು ಟೈಪ್ ಮಾಡಿ"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"ಸೂಚಿತ ಭಾಷೆ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ಎಲ್ಲಾ ಭಾಷೆಗಳು"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"ಎಲ್ಲಾ ಪ್ರದೇಶಗಳು"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"ಹುಡುಕು"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"ಕೆಲಸದ ಮೋಡ್ ಆಫ್ ಆಗಿದೆ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ಹಿನ್ನೆಲೆ ಸಿಂಕ್ ಮತ್ತು ಇತರ ಸಂಬಂಧಿತ ವೈಶಿಷ್ಟ್ಯಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌‌ ಕಾರ್ಯನಿರ್ವಹಿಸಲು ಅನುಮತಿಸಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index dc1da74..b9abc66 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>에 연결되어 있습니다. 네트워크를 관리하려면 누르세요."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"연결 유지 VPN에 연결하는 중…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"연결 유지 VPN에 연결됨"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"연결 유지 VPN 연결 해제됨"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"연결 유지 VPN 오류"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"탭하여 설정"</string>
     <string name="upload_file" msgid="2897957172366730416">"파일 선택"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"파일을 선택하지 않았습니다."</string>
     <string name="reset" msgid="2448168080964209908">"초기화"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"언어 이름 입력"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"추천"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"모든 언어"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"검색"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"직장 모드가 사용 중지됨"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"앱, 백그라운드 동기화 및 관련 기능을 포함한 직장 프로필이 작동하도록 허용"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index c1e415e..0cf149a 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> сеансына туташуу ишке ашты. Желенин параметрлерин өзгөртүү үчүн бул жерди басыңыз."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Дайым иштеген VPN туташууда…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Дайым иштеген VPN туташтырылды"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Дайым иштеген VPN ажыратылды"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Дайым иштеген VPN\'де ката кетти"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Жөндөө үчүн таптаңыз"</string>
     <string name="upload_file" msgid="2897957172366730416">"Файл тандоо"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Эч файл тандалган жок"</string>
     <string name="reset" msgid="2448168080964209908">"Баштапкы абалга келтирүү"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Тилди киргизиңиз"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Сунушталган"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Бардык тилдер"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Издөө"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Жумуш режими ӨЧҮРҮЛГӨН"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Жумуш профилин, ошондой эле колдонмолорду, фондо шайкештирүү жана ага байланыштуу функцияларды иштетиңиз."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index a6edcb0..35b2805 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"ເຊື່ອມຕໍ່ກັບ <xliff:g id="SESSION">%s</xliff:g> ແລ້ວ. ແຕະເພື່ອຈັດການເຄືອຂ່າຍ."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ກຳລັງເຊື່ອມຕໍ່ Always-on VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ເຊື່ອມຕໍ່ VPN ແບບເປີດຕະຫຼອດເວລາແລ້ວ"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"ຕັດການເຊື່ອມຕໍ່ VPN ແບບເປີດໃຊ້ຕະຫຼອດເວລາແລ້ວ"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"VPN ແບບເປີດຕະຫຼອດເກີດຄວາມຜິດພາດ"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"ແຕະເພື່ອຕັ້ງຄ່າ"</string>
     <string name="upload_file" msgid="2897957172366730416">"ເລືອກໄຟລ໌"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ບໍ່ໄດ້ເລືອກໄຟລ໌ເທື່ອ"</string>
     <string name="reset" msgid="2448168080964209908">"ຣີເຊັດ"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ພິມຊື່ພາສາ"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"ແນະນຳ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ທຸກພາ​ສາ​"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"ຄົ້ນຫາ"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"ໂໝດບ່ອນເຮັດວຽກປິດຢູ່"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"ອະນຸຍາດໃຫ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກສາມາດນຳໃຊ້ໄດ້ ເຊິ່ງຮວມທັງແອັບ, ການຊິ້ງຂໍ້ມູນໃນພື້ນຫຼັງ ແລະ ຄຸນສົມບັດທີ່ກ່ຽວຂ້ອງ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index eb62a70..233e03e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Prisijungta prie <xliff:g id="SESSION">%s</xliff:g>. Jei norite valdyti tinklą, palieskite."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prisijungiama prie visada įjungto VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Prisijungta prie visada įjungto VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Visada įjungtas VPN atjungtas"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Visada įjungto VPN klaida"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Palieskite, kad nustatytumėte"</string>
     <string name="upload_file" msgid="2897957172366730416">"Pasirinkti failą"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nepasirinktas joks failas"</string>
     <string name="reset" msgid="2448168080964209908">"Atstatyti"</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Įveskite kalbos pav."</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Siūloma"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Visos kalbos"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Visi regionai"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Paieška"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Darbo režimas išjungtas"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Leisti veikti darbo profiliui, įskaitant programas, sinchronizavimą fone ir susijusias funkcijas."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index c920470..58d46fa 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1268,11 +1268,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Ir izveidots savienojums ar: <xliff:g id="SESSION">%s</xliff:g>. Pieskarieties, lai pārvaldītu tīklu."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Notiek savienojuma izveide ar vienmēr ieslēgtu VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Izveidots savienojums ar vienmēr ieslēgtu VPN."</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Vienmēr ieslēgts VPN ir atvienots"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kļūda saistībā ar vienmēr ieslēgtu VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Pieskarieties, lai iestatītu."</string>
     <string name="upload_file" msgid="2897957172366730416">"Izvēlēties failu"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Neviens fails nav izvēlēts"</string>
     <string name="reset" msgid="2448168080964209908">"Atiestatīt"</string>
@@ -1686,6 +1684,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Ierakstiet valodas nosaukumu"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Ieteiktās"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Visas valodas"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Visi reģioni"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Meklēt"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Darba režīms IZSLĒGTS"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Atļaujiet darboties darba profilam, tostarp lietotnēm, sinhronizācijai fonā un saistītajām funkcijām."</string>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index 0f39e42..ddf0e9f 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -25,13 +25,6 @@
     -->
     <integer name="config_mobile_mtu">1358</integer>
 
-    <!-- service number convert map in roaming network. -->
-    <!-- [dialstring],[replacement][,optional gid] -->
-    <string-array translatable="false" name="dial_string_replace">
-        <item>"*611:+19085594899,BAE0000000000000"</item>
-        <item>"*86:+1MDN,BAE0000000000000"</item>
-    </string-array>
-
     <!-- Flag indicating whether strict threshold is used, or lenient threshold is used,
           when evaluating RSRP for LTE antenna bar display
            0. Strict threshold
diff --git a/core/res/res/values-mcc310-mnc120/config.xml b/core/res/res/values-mcc310-mnc120/config.xml
index 4b61688..413c698 100644
--- a/core/res/res/values-mcc310-mnc120/config.xml
+++ b/core/res/res/values-mcc310-mnc120/config.xml
@@ -25,9 +25,6 @@
     -->
     <integer name="config_mobile_mtu">1422</integer>
 
-    <!-- Sprint need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">70</integer>
-
     <!-- If this value is true, The mms content-disposition field is supported correctly.
          If false, Content-disposition fragments are ignored -->
     <bool name="config_mms_content_disposition_support">false</bool>
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index cddd5e3..9accdf0 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -67,7 +67,4 @@
         <item>"#8"</item>
         <item>"#9"</item>
     </string-array>
-    <!-- Flag indicating whether radio is to be restarted on the error of
-         PDP_FAIL_REGULAR_DEACTIVATION/0x24 -->
-    <bool name="config_restart_radio_on_pdp_fail_regular_deactivation">true</bool>
 </resources>
diff --git a/core/res/res/values-mcc311-mnc221/config.xml b/core/res/res/values-mcc311-mnc221/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc221/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc222/config.xml b/core/res/res/values-mcc311-mnc222/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc222/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc223/config.xml b/core/res/res/values-mcc311-mnc223/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc223/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc224/config.xml b/core/res/res/values-mcc311-mnc224/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc224/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc225/config.xml b/core/res/res/values-mcc311-mnc225/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc225/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc226/config.xml b/core/res/res/values-mcc311-mnc226/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc226/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc227/config.xml b/core/res/res/values-mcc311-mnc227/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc227/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc228/config.xml b/core/res/res/values-mcc311-mnc228/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc228/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc229/config.xml b/core/res/res/values-mcc311-mnc229/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc229/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 4e17e98..714b312 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -52,11 +52,6 @@
     <bool name="config_carrier_volte_provisioned">true</bool>
 
     <bool name="config_auto_attach_data_on_creation">false</bool>
-    <!-- service number convert map in roaming network. -->
-    <string-array translatable="false" name="dial_string_replace">
-        <item>"*611:+19085594899,"</item>
-        <item>"*86:+1MDN,"</item>
-    </string-array>
 
     <!-- Flag indicating whether strict threshold is used, or lenient threshold is used,
           when evaluating RSRP for LTE antenna bar display
@@ -70,11 +65,4 @@
     </string-array>
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
 
-    <!-- Network switching warnings use toasts. -->
-    <integer translatable="false" name="config_networkNotifySwitchType">2</integer>
-    <!-- Notify when switching from Wi-Fi to cellular data. -->
-    <string-array translatable="false" name="config_networkNotifySwitches">
-        <item>WIFI-CELLULAR</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc311-mnc490/config.xml b/core/res/res/values-mcc311-mnc490/config.xml
index d481c97..836abdf 100644
--- a/core/res/res/values-mcc311-mnc490/config.xml
+++ b/core/res/res/values-mcc311-mnc490/config.xml
@@ -21,9 +21,6 @@
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <!-- Sprint need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">70</integer>
-
     <!-- An array of CDMA roaming indicators which means international roaming -->
     <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
         <item>2</item>
diff --git a/core/res/res/values-mcc311-mnc580/config.xml b/core/res/res/values-mcc311-mnc580/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc580/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc581/config.xml b/core/res/res/values-mcc311-mnc581/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc581/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc582/config.xml b/core/res/res/values-mcc311-mnc582/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc582/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc583/config.xml b/core/res/res/values-mcc311-mnc583/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc583/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc584/config.xml b/core/res/res/values-mcc311-mnc584/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc584/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc585/config.xml b/core/res/res/values-mcc311-mnc585/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc585/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc586/config.xml b/core/res/res/values-mcc311-mnc586/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc586/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc587/config.xml b/core/res/res/values-mcc311-mnc587/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc587/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc588/config.xml b/core/res/res/values-mcc311-mnc588/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc588/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc589/config.xml b/core/res/res/values-mcc311-mnc589/config.xml
deleted file mode 100644
index 811e9c7..0000000
--- a/core/res/res/values-mcc311-mnc589/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc870/config.xml b/core/res/res/values-mcc311-mnc870/config.xml
index 98cb72e..f6aed13 100644
--- a/core/res/res/values-mcc311-mnc870/config.xml
+++ b/core/res/res/values-mcc311-mnc870/config.xml
@@ -25,9 +25,6 @@
     -->
     <integer name="config_mobile_mtu">1422</integer>
 
-    <!-- Sprint need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">70</integer>
-
     <!-- An array of CDMA roaming indicators which means international roaming -->
     <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
         <item>2</item>
diff --git a/core/res/res/values-mcc312-mnc530/config.xml b/core/res/res/values-mcc312-mnc530/config.xml
index 98cb72e..f6aed13 100644
--- a/core/res/res/values-mcc312-mnc530/config.xml
+++ b/core/res/res/values-mcc312-mnc530/config.xml
@@ -25,9 +25,6 @@
     -->
     <integer name="config_mobile_mtu">1422</integer>
 
-    <!-- Sprint need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">70</integer>
-
     <!-- An array of CDMA roaming indicators which means international roaming -->
     <integer-array translatable="false" name="config_cdma_international_roaming_indicators" >
         <item>2</item>
diff --git a/core/res/res/values-mcc311-mnc220/config.xml b/core/res/res/values-mcc722-mnc36/config.xml
similarity index 69%
rename from core/res/res/values-mcc311-mnc220/config.xml
rename to core/res/res/values-mcc722-mnc36/config.xml
index 811e9c7..daf5373 100644
--- a/core/res/res/values-mcc311-mnc220/config.xml
+++ b/core/res/res/values-mcc722-mnc36/config.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2014, The Android Open Source Project
+** 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.
@@ -17,10 +17,9 @@
 */
 -->
 
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- USC need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">300</integer>
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>72234</item>
+    </string-array>
 </resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 3b0729a..0573cc6 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Поврзани сте на <xliff:g id="SESSION">%s</xliff:g>. Допрете за да управувате со мрежата."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Поврзување со секогаш вклучена VPN..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Поврзани со секогаш вклучена VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Секогаш вклучената VPN е неповрзана"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка на секогаш вклучена VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Допрете за да поставите"</string>
     <string name="upload_file" msgid="2897957172366730416">"Избери датотека"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Не е избрана датотека"</string>
     <string name="reset" msgid="2448168080964209908">"Ресетирај"</string>
@@ -1652,6 +1650,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Внеси име на јазик"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Предложени"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Сите јазици"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Сите региони"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Пребарај"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Режимот на работа е ИСКЛУЧЕН"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Дозволете работниот профил да функционира, вклучувајќи ги апликациите, синхронизирањето во заднина и други поврзани функции."</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 237f3e2..01549ff 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> എന്ന സെഷനിലേക്ക് കണക്റ്റുചെയ്തു. നെറ്റ്‌വർക്ക് മാനേജുചെയ്യാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN കണക്റ്റുചെയ്യുന്നു…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN കണക്റ്റുചെയ്‌തു"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"\'എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN\' വിച്ഛേദിച്ചു"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"എല്ലായ്‌പ്പോഴും ഓണായിരിക്കുന്ന VPN പിശക്"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക"</string>
     <string name="upload_file" msgid="2897957172366730416">"ഫയല്‍‌ തിരഞ്ഞെടുക്കുക"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ഫയലൊന്നും തിരഞ്ഞെടുത്തില്ല"</string>
     <string name="reset" msgid="2448168080964209908">"പുനഃസജ്ജമാക്കുക"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ഭാഷയുടെ പേര് ടൈപ്പുചെയ്യുക"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"നിര്‍‌ദ്ദേശിച്ചത്"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"എല്ലാ ഭാഷകളും"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"തിരയുക"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"ഔദ്യോഗിക മോഡ് ഓഫാണ്"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"ആപ്സും, പശ്ചാത്തല സമന്വയവും ബന്ധപ്പെട്ട ഫീച്ചറുകളും ഉൾപ്പെടെ, ഔദ്യോഗിക പ്രൊഫൈലിനെ പ്രവർത്തിക്കാൻ അനുവദിക്കുക."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 1a05ebe..1f35690 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>-д холбогдов. Сүлжээг удирдах бол товшино уу."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Байнгын VPN-д холбогдож байна..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Байнга VPN холбоотой"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Тогтмол асаалттай VPN салсан"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Байнгын VPN алдаа"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Тохируулахын тулд товшино уу"</string>
     <string name="upload_file" msgid="2897957172366730416">"Файл сонгох"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Сонгосон файл байхгүй"</string>
     <string name="reset" msgid="2448168080964209908">"Бүгдийг цэвэрлэх"</string>
@@ -1648,6 +1646,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Улсын хэлийг бичнэ үү"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Санал болгосон"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Бүх хэл"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Бүх бүс нутаг"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Хайх"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Ажлын горимыг УНТРААСАН байна"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Ажлын профайлд апп, дэвсгэр синхрончлол болон бусад холбоотой тохиргоог ажиллахыг зөвшөөрнө үү."</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 9334a6e..278d4ad 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> शी कनेक्ट केले. नेटवर्क व्यवस्थापित करण्यासाठी टॅप करा."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN कनेक्ट करणे नेहमी-चालू…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN कनेक्ट केलेले नेहमी-चालू"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"नेहमी-चालू असलेले VPN डिस्कनेक्ट केले"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"VPN त्रुटी नेहमी-चालू"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"सेट करण्यासाठी टॅप करा"</string>
     <string name="upload_file" msgid="2897957172366730416">"फाईल निवडा"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"फाईल निवडली नाही"</string>
     <string name="reset" msgid="2448168080964209908">"रीसेट करा"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"भाषा नाव टाइप करा"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"सूचित केलेले"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"सर्व भाषा"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"सर्व प्रदेश"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"शोध"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"कार्य मोड बंद आहे"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"कार्य प्रोफाइलला अॅप्स, पार्श्वभूमी संकालन आणि संबंधित वैशिष्ट्यांच्या समावेशासह कार्य करण्याची परवानगी द्या."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 2d2246e..268bca4 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Bersambung kepada <xliff:g id="SESSION">%s</xliff:g>. Ketik untuk mengurus rangkaian."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sentiasa hidup sedang disambungkan..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sentiasa hidup telah disambungkan"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN sentiasa hidup diputuskan sambungannya"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ralat VPN sentiasa hidup"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Ketik untuk menyediakan"</string>
     <string name="upload_file" msgid="2897957172366730416">"Pilih fail"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Tiada fail dipilih"</string>
     <string name="reset" msgid="2448168080964209908">"Tetapkan semula"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Taipkan nama bahasa"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Dicadangkan"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Semua bahasa"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Cari"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Mod kerja DIMATIKAN"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Benarkan profil kerja berfungsi, termasuk apl, penyegerakan latar belakang dan ciri yang berkaitan."</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 78a4153..b6d42bc 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> သို့ ချိတ်ဆက်ထားသည်။ ကွန်ရက်ကို စီမံခန့်ခွဲရန် တို့ပါ။"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"အမြဲတမ်းဖွင့်ထား VPN ဆက်သွယ်နေစဉ်…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"အမြဲတမ်းဖွင့်ထား VPN ဆက်သွယ်မှုရှိ"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"အမြဲတမ်းဖွင့်ထားရသော VPN ပြတ်တောက်နေသည်"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"အမြဲတမ်းဖွင့်ထား VPN အမှား"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"ပြင်ဆင်သတ်မှတ်ရန် တို့ပါ"</string>
     <string name="upload_file" msgid="2897957172366730416">"ဖိုင်ရွေးချယ်ရန်"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"မည်သည့်ဖိုင်ကိုမှမရွေးပါ"</string>
     <string name="reset" msgid="2448168080964209908">"ပြန်လည်သတ်မှတ်ရန်"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ဘာသာစကားအမည် ထည့်ပါ"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"အကြံပြုထားသော"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ဘာသာစကားများအားလုံး"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"ဒေသအားလုံး"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"ရှာဖွေရန်"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"အလုပ်မုဒ် ပိတ်ထားသည်"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"အက်ပ်များ၊ နောက်ခံစင့်ခ်လုပ်ခြင်း၊ နှင့်သက်ဆိုင်သည့်အင်္ဂါရပ်များကို ဆောင်ရွက်ရန် အလုပ်ပရိုဖိုင်ကိုခွင့်ပြုပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ef799ea..ffe3001 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Koblet til <xliff:g id="SESSION">%s</xliff:g>. Trykk for å administrere nettverket."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Alltid-på VPN kobler til ..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Alltid-på VPN er tilkoblet"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Alltid på-VPN er frakoblet"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Alltid-på VPN-feil"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Trykk for å konfigurere"</string>
     <string name="upload_file" msgid="2897957172366730416">"Velg fil"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
     <string name="reset" msgid="2448168080964209908">"Tilbakestill"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Skriv inn språknavn"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Foreslått"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Alle språk"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Alle områder"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Søk"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Jobbmodus er AV"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Slå på jobbprofilen, inkludert apper, synkronisering i bakgrunnen og relaterte funksjoner."</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index e83449f..6b600a6 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -1249,11 +1249,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>सँग जोडिएको। नेटवर्क प्रबन्ध गर्न हान्नुहोस्।"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN जडान सधै जोड्दै…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"सधैँ खुल्ला हुने VPN जोडिएको"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"सधैँ-सक्रिय VPN लाई विच्छेद गरियो"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"सधैँ भरि VPN त्रुटिमा"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"सेट अप गर्न ट्याप गर्नुहोस्"</string>
     <string name="upload_file" msgid="2897957172366730416">"फाइल छान्नुहोस्"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"कुनै फाइल छानिएको छैन"</string>
     <string name="reset" msgid="2448168080964209908">"पुनःसेट गर्नु"</string>
@@ -1656,6 +1654,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"भाषाको नाम टाइप गर्नुहोस्"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"सुझाव दिइयो"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"सम्पूर्ण भाषाहरू"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"सबै क्षेत्रहरू"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"खोज"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"कार्य मोड बन्द छ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"अनुप्रयोग, पृष्ठभूमि सिंक र सम्बन्धित विशेषताहरू सहित, कार्य प्रोफाइललाई कार्य गर्न अनुमति दिनुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 492647f..0583e8d 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Verbonden met <xliff:g id="SESSION">%s</xliff:g>. Tik om het netwerk te beheren."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Always-on VPN-verbinding maken…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN-verbinding"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Always-on VPN-verbinding ontkoppeld"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fout met Always-on VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tik om in te stellen"</string>
     <string name="upload_file" msgid="2897957172366730416">"Bestand kiezen"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Geen bestand geselecteerd"</string>
     <string name="reset" msgid="2448168080964209908">"Resetten"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Typ een taalnaam"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Voorgesteld"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Alle talen"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Alle regio\'s"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Zoeken"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Werkmodus is UIT"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Functioneren van werkprofiel toestaan, waaronder apps, synchronisatie op de achtergrond en gerelateerde functies."</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 0b2af1e..9d8c3fc 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ। ਨੈੱਟਵਰਕ ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ਹਮੇਸ਼ਾਂ-ਚਾਲੂ VPN ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ਹਮੇਸ਼ਾਂ-ਚਾਲੂ VPN ਕਨੈਕਟ ਕੀਤਾ"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"ਹਮੇਸ਼ਾ-ਚਾਲੂ VPN ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"ਹਮੇਸ਼ਾਂ-ਚਾਲੂ VPN ਅਸ਼ੁੱਧੀ"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="upload_file" msgid="2897957172366730416">"ਫਾਈਲ ਚੁਣੋ"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ਕੋਈ ਫਾਈਲ ਨਹੀਂ ਚੁਣੀ ਗਈ"</string>
     <string name="reset" msgid="2448168080964209908">"ਰੀਸੈੱਟ ਕਰੋ"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"ਭਾਸ਼ਾ ਨਾਮ ਟਾਈਪ ਕਰੋ"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"ਸੁਝਾਈਆਂ ਗਈਆਂ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ਸਾਰੀਆਂ ਭਾਸ਼ਾਵਾਂ"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"ਸਾਰੇ ਖੇਤਰ"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"ਖੋਜ"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"ਕੰਮ ਮੋਡ ਬੰਦ ਹੈ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"ਐਪਾਂ, ਬੈਕਗ੍ਰਾਊਂਡ ਸਮਕਾਲੀਕਰਨ, ਅਤੇ ਸਬੰਧਿਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਸ਼ਾਮਲ ਕਰਦੇ ਹੋਏ ਕੰਮ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਕੰਮ ਕਰਨ ਦੀ ਮਨਜ਼ੂਰੀ ਦਿਓ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6d7d91e..3a26d64 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Nawiązano połączenie: <xliff:g id="SESSION">%s</xliff:g>. Dotknij, aby zarządzać siecią."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Łączę ze stałą siecią VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Połączono ze stałą siecią VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Rozłączono ze stałą siecią VPN"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Błąd stałej sieci VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Kliknij, by skonfigurować"</string>
     <string name="upload_file" msgid="2897957172366730416">"Wybierz plik"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nie wybrano pliku"</string>
     <string name="reset" msgid="2448168080964209908">"Resetuj"</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Wpisz nazwę języka"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugerowane"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Wszystkie języki"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Wszystkie kraje"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Szukaj"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Tryb pracy jest WYŁĄCZONY"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Włącz profil do pracy, w tym aplikacje, synchronizację w tle i inne funkcje."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 96ff854..eb4db3e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toque para gerenciar a rede."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sempre ativa conectando..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre ativa conectada"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN sempre ativa desconectada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro na VPN sempre ativa"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Toque para configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Escolher arquivo"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nenhum arquivo escolhido"</string>
     <string name="reset" msgid="2448168080964209908">"Redefinir"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Digitar nome do idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugeridos"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Todos os idiomas"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Pesquisa"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modo de trabalho DESATIVADO"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permitir que o perfil de trabalho funcione, incluindo apps, sincronização em segundo plano e recursos relacionados"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 0fc2a09..71107d6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Ligado a <xliff:g id="SESSION">%s</xliff:g>. Toque para gerir a rede."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"A ligar VPN sempre ativa..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre ativa ligada"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN sempre ativa desligada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro da VPN sempre ativa"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tocar para configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Escolher ficheiro"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Não foi selecionado nenhum ficheiro"</string>
     <string name="reset" msgid="2448168080964209908">"Repor"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Intr. nome do idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugeridos"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Todos os idiomas"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Todas as regiões"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Pesquisa"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modo de trabalho DESATIVADO"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permitir o funcionamento do perfil de trabalho, incluindo as aplicações, a sincronização em segundo plano e as funcionalidades relacionadas."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 96ff854..eb4db3e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toque para gerenciar a rede."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sempre ativa conectando..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre ativa conectada"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"VPN sempre ativa desconectada"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro na VPN sempre ativa"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Toque para configurar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Escolher arquivo"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nenhum arquivo escolhido"</string>
     <string name="reset" msgid="2448168080964209908">"Redefinir"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Digitar nome do idioma"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugeridos"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Todos os idiomas"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Pesquisa"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modo de trabalho DESATIVADO"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permitir que o perfil de trabalho funcione, incluindo apps, sincronização em segundo plano e recursos relacionados"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 2361e82..ce00c0e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1268,11 +1268,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Apăsați pentru a gestiona rețeaua."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Se efectuează conectarea la rețeaua VPN activată permanent…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Conectat(ă) la rețeaua VPN activată permanent"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Rețeaua VPN activată permanent a fost deconectată"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Eroare de rețea VPN activată permanent"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Atingeți pentru a configura"</string>
     <string name="upload_file" msgid="2897957172366730416">"Alegeți un fișier"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fișiere"</string>
     <string name="reset" msgid="2448168080964209908">"Resetați"</string>
@@ -1686,6 +1684,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Numele limbii"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugerate"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Toate limbile"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Toate regiunile"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Căutați"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modul de serviciu e DEZACTIVAT"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Permiteți profilului de serviciu să funcționeze, inclusiv aplicațiile, sincronizarea în fundal și funcțiile asociate."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index daf2eb8..377476a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Подключено: \"<xliff:g id="SESSION">%s</xliff:g>\". Нажмите здесь, чтобы изменить настройки сети."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Подключение…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Подключено"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Отключено"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ошибка"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Нажмите, чтобы настроить"</string>
     <string name="upload_file" msgid="2897957172366730416">"Выбрать файл"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Не выбран файл"</string>
     <string name="reset" msgid="2448168080964209908">"Сбросить"</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Введите язык"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Рекомендуемые"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Все языки"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Все регионы"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Поиск"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Рабочий режим отключен"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Включить рабочий профиль: приложения, фоновую синхронизацию и связанные функции."</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index e14bb63..014cdac 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -1245,11 +1245,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> වෙත සම්බන්ධිතයි. ජාලය කළමනාකරණය කිරීමට තට්ටු කරන්න."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"සැමවිටම VPN සම්බන්ධ වෙමින්…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"නිරතුරුවම VPN සම්බන්ධ කර ඇත"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"සැමවිට ක්‍රියාත්මක VPN විසන්ධි කරන ලදී"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"සැමවිට සක්‍රිය VPN දෝෂය"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"පිහිටුවීමට තට්ටු කරන්න"</string>
     <string name="upload_file" msgid="2897957172366730416">"ගොනුව තෝරන්න"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ගොනුවක් තෝරාගෙන නැත"</string>
     <string name="reset" msgid="2448168080964209908">"යළි පිහිටුවන්න"</string>
@@ -1652,6 +1650,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"භාෂා නම ටයිප් කරන්න"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"යෝජිත"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"සියලු භාෂා"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"සියලු ප්‍රදේශ"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"සෙවීම"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"වැඩ ප්‍රකාරය ක්‍රියාවිරහිතයි"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"යෙදුම්, පසුබිම සමමුහුර්ත කිරීම, සහ සම්බන්ධිත විශේෂාංග ඇතුළුව, ක්‍රියා කිරීමට කාර්යාල පැතිකඩට ඉඩ දෙන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7dc5116..4adbe4b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Pripojené k relácii <xliff:g id="SESSION">%s</xliff:g>. Po klepnutí môžete sieť spravovať."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Pripájanie k vždy zapnutej sieti VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Pripojenie k vždy zapnutej sieti VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Vždy zapnutá sieť VPN bola odpojená"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba vždy zapnutej siete VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Klepnutím prejdete do Nastavení"</string>
     <string name="upload_file" msgid="2897957172366730416">"Zvoliť súbor"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nie je vybratý žiadny súbor"</string>
     <string name="reset" msgid="2448168080964209908">"Obnoviť"</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Zadajte názov jazyka"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Navrhované"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Všetky jazyky"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Všetky regióny"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Vyhľadávanie"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Pracovný režim je VYPNUTÝ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Povoľte fungovanie pracovného profilu vrátane aplikácií, synchronizácie na pozadí a súvisiacich funkcií."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5785167..45cd01d 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Povezan z mestom <xliff:g id="SESSION">%s</xliff:g>. Tapnite za upravljanje omrežja."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezovanje v stalno vklopljeno navidezno zasebno omrežje ..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Vzpostavljena povezava v stalno vklopljeno navidezno zasebno omrežje"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Povezava s stalno vklopljenim VPN-jem je prekinjena"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Napaka stalno vklopljenega navideznega zasebnega omrežja"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Dotaknite se, če želite nastaviti"</string>
     <string name="upload_file" msgid="2897957172366730416">"Izberi datoteko"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nobena datoteka ni izbrana"</string>
     <string name="reset" msgid="2448168080964209908">"Ponastavi"</string>
@@ -1722,6 +1720,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Vnesite ime jezika"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Predlagano"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Vsi jeziki"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Išči"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Delovni način IZKLOPLJEN"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Dovoljeno delovanje delovnega profila, vključno z aplikacijami, sinhronizacijo v ozadju in povezanimi funkcijami."</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index df3b9e0..74060d7 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Lidhur me <xliff:g id="SESSION">%s</xliff:g>. Trokit për të menaxhuar rrjetin."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Po lidh VPN-në për aktivizim të përhershëm…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN e lidhur në mënyrë të përhershme"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Rrjeti VPN gjithmonë aktiv u shkëput"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Gabimi VPN-je për aktivizimin e përhershëm"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Trokit për ta konfiguruar"</string>
     <string name="upload_file" msgid="2897957172366730416">"Zgjidh skedarin"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nuk u zgjodh asnjë skedar"</string>
     <string name="reset" msgid="2448168080964209908">"Rivendos"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Shkruaj emrin e gjuhës"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Sugjeruar"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Të gjitha gjuhët"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Të gjitha rajonet"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Kërko"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Modaliteti i punës është JOAKTIV"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Lejoje profilin e punës të funksionojë, duke përfshirë aplikacionet, sinkronizimin në sfond dhe funksionet e lidhura."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 790eb8b..f31a6a1 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1268,11 +1268,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Повезано са сесијом <xliff:g id="SESSION">%s</xliff:g>. Додирните да бисте управљали мрежом."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Повезивање стално укљученог VPN-а..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Стално укључени VPN је повезан"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Веза са стално укљученим VPN-ом је прекинута"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка стално укљученог VPN-а"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Додирните да бисте подесили"</string>
     <string name="upload_file" msgid="2897957172366730416">"Одабери датотеку"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Није изабрана ниједна датотека"</string>
     <string name="reset" msgid="2448168080964209908">"Поново постави"</string>
@@ -1686,6 +1684,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Унесите назив језика"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Предложени"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Сви језици"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Претражи"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Режим за Work је ИСКЉУЧЕН"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Дозвољава профилу за Work да функционише, укључујући апликације, синхронизацију у позадини и сродне функције."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 22d18a8..3b79391 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Ansluten till <xliff:g id="SESSION">%s</xliff:g>. Knacka lätt för att hantera nätverket."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ansluter till Always-on VPN ..."</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ansluten till Always-on VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Always-on VPN har kopplats från"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fel på Always-on VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tryck för att konfigurera"</string>
     <string name="upload_file" msgid="2897957172366730416">"Välj fil"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil har valts"</string>
     <string name="reset" msgid="2448168080964209908">"Återställ"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Ange språket"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Förslag"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Alla språk"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Alla regioner"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Söka"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Arbetsläget är inaktiverat"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Tillåt att jobbprofilen är aktiv, inklusive appar, bakgrundssynkronisering och andra tillhörande funktioner."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 470a6cf..f9a077e 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1241,11 +1241,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gonga ili kudhibiti mtandao"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kila mara VPN iliyowashwa inaunganishwa…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Kila mara VPN iliyowashwa imeunganishwa"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Iwe imeondoa VPN kila wakati"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kila mara kuna hitilafu ya VPN iliyowashwa"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Gonga ili uweke mipangilio"</string>
     <string name="upload_file" msgid="2897957172366730416">"Chagua faili"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Hakuna faili iliyochaguliwa"</string>
     <string name="reset" msgid="2448168080964209908">"Weka upya"</string>
@@ -1648,6 +1646,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Weka jina la lugha"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Inayopendekezwa"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Lugha zote"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Tafuta"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Hali ya kazi IMEZIMWA"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Ruhusu wasifu wa kazini utumike, ikiwa ni pamoja na programu, usawazishaji wa chini chini na vipengele vinavyohusiana."</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 1a7b944..c6386ac 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> உடன் இணைக்கப்பட்டது. நெட்வொர்க்கை நிர்வகிக்க, தட்டவும்."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"எப்போதும் இயங்கும் VPN உடன் இணைக்கிறது…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"எப்போதும் இயங்கும் VPN இணைக்கப்பட்டது"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"எப்போதும் இயங்கும் VPN துண்டிக்கப்பட்டது"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"எப்போதும் இயங்கும் VPN பிழை"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"அமைக்க, தட்டவும்"</string>
     <string name="upload_file" msgid="2897957172366730416">"கோப்பைத் தேர்வுசெய்"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"எந்தக் கோப்பும் தேர்வுசெய்யப்படவில்லை"</string>
     <string name="reset" msgid="2448168080964209908">"மீட்டமை"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"மொழி பெயரை உள்ளிடுக"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"பரிந்துரைகள்"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"எல்லா மொழிகளும்"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"எல்லா மண்டலங்களும்"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"தேடு"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"பணிப் பயன்முறை முடக்கப்பட்டது"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"செயல்பட, பணி சுயவிவரத்தை அனுமதி. இதில் பயன்பாடுகள், பின்னணி ஒத்திசைவு மற்றும் தொடர்புடைய அம்சங்கள் அடங்கும்."</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index e2aa9d2..fb0c7a0 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g>కు కనెక్ట్ చేయబడింది. నెట్‌వర్క్‌ను నిర్వహించడానికి నొక్కండి."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ఎల్లప్పుడూ-ఆన్‌లో ఉండే VPN కనెక్ట్ చేయబడుతోంది…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ఎల్లప్పుడూ-ఆన్‌లో ఉండే VPN కనెక్ట్ చేయబడింది"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"ఎల్లప్పుడూ ఆన్‌లో ఉండే VPN డిస్‌కనెక్ట్ చేయబడింది"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"ఎల్లప్పుడూ-ఆన్‌లో ఉండే VPN లోపం"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"సెటప్ చేయడానికి నొక్కండి"</string>
     <string name="upload_file" msgid="2897957172366730416">"ఫైల్‌ను ఎంచుకోండి"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ఫైల్ ఎంచుకోబడలేదు"</string>
     <string name="reset" msgid="2448168080964209908">"రీసెట్ చేయి"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"భాష పేరును టైప్ చేయండి"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"సూచించినవి"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"అన్ని భాషలు"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"అన్ని ప్రాంతాలు"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"శోధించు"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"కార్యాలయ మోడ్ ఆఫ్ చేయబడింది"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"అనువర్తనాలు, నేపథ్య సమకాలీకరణ మరియు సంబంధిత లక్షణాలతో సహా కార్యాలయ ప్రొఫైల్‌ను పని చేయడానికి అనుమతించండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 67b629a..f876216 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"เชื่อมต่อกับ <xliff:g id="SESSION">%s</xliff:g> แตะเพื่อจัดการเครือข่าย"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"กำลังเชื่อมต่อ VPN แบบเปิดตลอดเวลา…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"เชื่อมต่อ VPN แบบเปิดตลอดเวลาแล้ว"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"ยกเลิกการเชื่อมต่อ VPN แบบเปิดตลอดเวลาแล้ว"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"ข้อผิดพลาดของ VPN แบบเปิดตลอดเวลา"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"แตะเพื่อตั้งค่า"</string>
     <string name="upload_file" msgid="2897957172366730416">"เลือกไฟล์"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"ไม่ได้เลือกไฟล์ไว้"</string>
     <string name="reset" msgid="2448168080964209908">"รีเซ็ต"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"พิมพ์ชื่อภาษา"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"แนะนำ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"ทุกภาษา"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"ภูมิภาคทั้งหมด"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"ค้นหา"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"โหมดทำงานปิดอยู่"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"อนุญาตให้โปรไฟล์งานทำงานได้ ซึ่งรวมถึงแอป การซิงค์ในพื้นหลัง และคุณลักษณะอื่นที่เกี่ยวข้อง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 162ef52..3397d8d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Nakakonekta sa <xliff:g id="SESSION">%s</xliff:g>. Tapikin upang pamahalaan ang network."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kumukonekta ang Always-on VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Nakakonekta ang Always-on VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Hindi nakakonekta ang palaging naka-on na VPN"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error sa Always-on VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"I-tap upang i-set up"</string>
     <string name="upload_file" msgid="2897957172366730416">"Pumili ng file"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Walang napiling file"</string>
     <string name="reset" msgid="2448168080964209908">"I-reset"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"I-type ang wika"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Iminumungkahi"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Lahat ng wika"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Lahat ng rehiyon"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Maghanap"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"NAKA-OFF ang work mode"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Payagang gumana ang profile sa trabaho, kasama na ang mga app, pag-sync sa background at mga may kaugnayang feature."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 93d3d23..a31fa47 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> oturumuna bağlı. Ağı yönetmek için hafifçe vurun."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Her zaman açık VPN\'ye bağlanılıyor…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Her zaman açık VPN\'ye bağlanıldı"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Her zaman açık VPN bağlantısı kesildi"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Her zaman açık VPN hatası"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Ayarlamak için dokunun"</string>
     <string name="upload_file" msgid="2897957172366730416">"Dosya seç"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Seçili dosya yok"</string>
     <string name="reset" msgid="2448168080964209908">"Sıfırla"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Dil adını yazın"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Önerilen"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Tüm diller"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Ara"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"İş modu KAPALI"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Uygulamalar, arka planda senkronizasyon ve ilgili özellikler dahil olmak üzere iş profilinin çalışmasına izin ver."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 37b413c..3976558 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1293,11 +1293,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Під’єднано до <xliff:g id="SESSION">%s</xliff:g>. Торкніться, щоб керувати мережею."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Під’єднання до постійної мережі VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Під’єднано до постійної мережі VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Постійну мережу VPN від’єднано"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Помилка постійної мережі VPN"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Торкніться, щоб налаштувати"</string>
     <string name="upload_file" msgid="2897957172366730416">"Виберіть файл"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Не вибрано файл"</string>
     <string name="reset" msgid="2448168080964209908">"Віднов."</string>
@@ -1722,6 +1720,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Введіть назву мови"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Пропоновані"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Усі мови"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Усі регіони"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Пошук"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Робочий профіль ВИМКНЕНО"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Увімкнути робочий профіль, зокрема додатки, фонову синхронізацію та пов’язані функції."</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 8f3ceca..4bf8b52 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> سے منسلک ہے۔ نیٹ ورک کا نظم کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"‏ہمیشہ آن VPN مربوط ہو رہا ہے…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"‏ہمیشہ آن VPN مربوط ہوگیا"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"‏ہمیشہ آن VPN غیر منسلک ہو گیا"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"‏ہمیشہ آن VPN کی خرابی"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"سیٹ اپ کرنے کیلئے تھپتھپائیں"</string>
     <string name="upload_file" msgid="2897957172366730416">"فائل منتخب کریں"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"کوئی فائل منتخب نہیں کی گئی"</string>
     <string name="reset" msgid="2448168080964209908">"دوبارہ ترتیب دیں"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"زبان کا نام ٹائپ کریں"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"تجویز کردہ"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"سبھی زبانیں"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"تلاش"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"کام موڈ آف ہے"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"دفتری پروفائل کو کام کرنے دیں، بشمول ایپس، پس منظر کی مطابقت پذیری اور متعلقہ خصوصیات۔"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index cdd7979..ae7d547 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> ulandi. Tarmoq sozlamalarini o‘zgartirish uchun bu yerni bosing."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ulanmoqda…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ulandi"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Doimiy VPN o‘chirildi"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Xato"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Sozlash uchun bosing"</string>
     <string name="upload_file" msgid="2897957172366730416">"Faylni tanlash"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Hech qanday fayl tanlanmadi"</string>
     <string name="reset" msgid="2448168080964209908">"Asliga qaytarish"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Til nomini kiriting"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Taklif etiladi"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Barcha tillar"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"Qidiruv"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Ish rejimi O‘CHIQ"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Ishchi profilini yoqish: ilovalar, fonda sinxronlash va bog‘liq funksiyalar."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e9419c4..b0df78b 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Đã kết nối với <xliff:g id="SESSION">%s</xliff:g>. Chạm để quản lý mạng."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Đang kết nối VPN luôn bật…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Đã kết nối VPN luôn bật"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Đã ngắt kết nối VPN luôn bật"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Lỗi VPN luôn bật"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Nhấn để thiết lập"</string>
     <string name="upload_file" msgid="2897957172366730416">"Chọn tệp"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Không có tệp nào được chọn"</string>
     <string name="reset" msgid="2448168080964209908">"Đặt lại"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Nhập tên ngôn ngữ"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Ðược đề xuất"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Tất cả ngôn ngữ"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Tất cả khu vực"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Tìm kiếm"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Chế độ làm việc đang TẮT"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Cho phép hồ sơ công việc hoạt động, bao gồm ứng dụng, đồng bộ hóa trong nền và các tính năng liên quan."</string>
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/core/res/res/values-watch/donottranslate.xml
similarity index 63%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to core/res/res/values-watch/donottranslate.xml
index 402a536..d247ff6 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/core/res/res/values-watch/donottranslate.xml
@@ -13,9 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+
+<resources>
+    <!-- DO NOT TRANSLATE Spans within this text are applied to style composing regions
+    within an EditText widget. The text content is ignored and not used.
+    Note: This is @color/material_deep_teal_200, cannot use @color references here. -->
+    <string name="candidates_style" translatable="false"><font color="#80cbc4">candidates</font></string>
+ </resources>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index d09119f..8a080d9c 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -68,8 +68,6 @@
         <item name="breakStrategy">balanced</item>
     </style>
 
-    <style name="Widget.Material.ButtonBar" parent="Widget.Material.BaseButtonBar" />
-
     <style name="TextAppearance.Material.NumberPicker" parent="TextAppearance.Material.Body1">
         <item name="textSize">@dimen/text_size_medium_material</item>
     </style>
@@ -103,9 +101,4 @@
         <item name="gravity">@integer/config_dialogTextGravity</item>
         <item name="ellipsize">end</item>
     </style>
-
-    <!-- DO NOTE TRANSLATE Spans within this text are applied to style composing regions
-    within an EditText widget. The text content is ignored and not used.
-    Note: This is @color/material_deep_teal_200, cannot use @color references here. -->
-    <string name="candidates_style"><font color="#80cbc4">candidates</font></string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5c239d8..c2bf810 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"已连接到<xliff:g id="SESSION">%s</xliff:g>。点按即可管理网络。"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在连接到始终开启的 VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已连接到始终开启的 VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"始终开启的 VPN 已断开连接"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"始终开启的 VPN 出现错误"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"点按即可进行设置"</string>
     <string name="upload_file" msgid="2897957172366730416">"选择文件"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"未选定任何文件"</string>
     <string name="reset" msgid="2448168080964209908">"重置"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"输入语言名称"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"建议语言"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"所有语言"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"搜索"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"工作模式已关闭"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"启用工作资料,包括应用、后台同步和相关功能。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 323668a..c8921fd 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"已連線至 <xliff:g id="SESSION">%s</xliff:g>,輕按一下即可管理網絡。"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在連線至永久連線的 VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已連線至永久連線的 VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"永久連線的 VPN 已中斷"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"永久連線的 VPN 發生錯誤"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"輕按即可設定"</string>
     <string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"未選擇檔案"</string>
     <string name="reset" msgid="2448168080964209908">"重設"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"輸入語言名稱"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"建議"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"所有語言"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"所有國家/地區"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"搜尋"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"工作模式已關閉"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"允許使用應用程式、背景同步及相關功能的工作設定檔。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6532f10..ea53582 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"已連線至 <xliff:g id="SESSION">%s</xliff:g>,輕觸一下即可管理網路。"</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在連線至永久連線的 VPN…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已連線至永久連線的 VPN"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"永久連線的 VPN 已中斷連線"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"永久連線的 VPN 發生錯誤"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"輕觸即可進行設定"</string>
     <string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
     <string name="reset" msgid="2448168080964209908">"重設"</string>
@@ -1650,6 +1648,8 @@
     <string name="search_language_hint" msgid="7042102592055108574">"請輸入語言名稱"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"建議語言"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"所有語言"</string>
+    <!-- no translation found for region_picker_section_all (8966316787153001779) -->
+    <skip />
     <string name="locale_search_menu" msgid="2560710726687249178">"搜尋"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Work 模式已關閉"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"啟用 Work 設定檔,包括應用程式、背景同步處理和相關功能。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index c97541a..22f2850 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1243,11 +1243,9 @@
     <string name="vpn_text_long" msgid="4907843483284977618">"Ixhume ku-<xliff:g id="SESSION">%s</xliff:g>. Thepha ukuphatha inethiwekhi."</string>
     <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"I-VPN ehlala ikhanya iyaxhuma…"</string>
     <string name="vpn_lockdown_connected" msgid="8202679674819213931">"I-VPN ehlala ikhanya ixhunyiwe"</string>
-    <!-- no translation found for vpn_lockdown_disconnected (4532298952570796327) -->
-    <skip />
+    <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Njalo kuvuliwe i-VPN kunqamukile"</string>
     <string name="vpn_lockdown_error" msgid="6009249814034708175">"Iphutha le-VPN ehlala ikhanya"</string>
-    <!-- no translation found for vpn_lockdown_config (5099330695245008680) -->
-    <skip />
+    <string name="vpn_lockdown_config" msgid="5099330695245008680">"Thepha ukuze usethe"</string>
     <string name="upload_file" msgid="2897957172366730416">"Khetha ifayela"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Ayikho ifayela ekhethiwe"</string>
     <string name="reset" msgid="2448168080964209908">"Setha kabusha"</string>
@@ -1650,6 +1648,7 @@
     <string name="search_language_hint" msgid="7042102592055108574">"Thayipha igama lolimi"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Okuphakanyisiwe"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Zonke izilimi"</string>
+    <string name="region_picker_section_all" msgid="8966316787153001779">"Zonke izifunda"</string>
     <string name="locale_search_menu" msgid="2560710726687249178">"Sesha"</string>
     <string name="work_mode_off_title" msgid="8954725060677558855">"Imodi yomsebenzi IVALIWE"</string>
     <string name="work_mode_off_message" msgid="3286169091278094476">"Vumela iphrofayela yomsebenzi ukuze isebenze, efaka izinhlelo zokusebenza, ukuvumelanisa kwangemuva, nezici ezisondelene."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9940f28..aca2bcc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6965,9 +6965,11 @@
         <attr name="icon" />
         <!-- The key to store the Preference value. -->
         <attr name="key" format="string" />
-        <!-- The title for the Preference in a PreferenceActivity screen. -->
+        <!-- The title for the Preference. In API 25 and earlier, this value is read as a
+         plain string with styling information stripped. -->
         <attr name="title" />
-        <!-- The summary for the Preference in a PreferenceActivity screen. -->
+        <!-- The summary for the Preference. In API 25 and earlier, this value is read as a
+         plain string with styling information stripped. -->
         <attr name="summary" />
         <!-- The order for the Preference (lower values are to be ordered first). If this is not
              specified, the default ordering will be alphabetic. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 71e7977..e9e7d53 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -275,6 +275,11 @@
     <string-array translatable="false" name="config_networkNotifySwitches">
     </string-array>
 
+    <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+         Internet access. Actual device behaviour is controlled by
+         Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+    <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
+
     <!-- List of regexpressions describing the interface (if any) that represent tetherable
          USB interfaces.  If the device doesn't want to support tethering over USB this should
          be empty.  An example would be "usb.*" -->
@@ -2261,6 +2266,9 @@
         <item>SUPL_MODE=1</item>
     </string-array>
 
+    <!-- Sprint need a 70 ms delay for 3way call -->
+    <integer name="config_cdma_3waycall_flash_delay">0</integer>
+
     <!-- If there is no preload VM number in the sim card, carriers such as
          Verizon require to load a default vm number from the configurantion.
          Define config_default_vm_number for this purpose. And there are two
@@ -2276,9 +2284,6 @@
          current sim's gid, the last one without gid will be picked -->
     <string-array translatable="false" name="config_default_vm_number" />
 
-    <!-- Sprint need a 70 ms delay for 3way call -->
-    <integer name="config_cdma_3waycall_flash_delay">0</integer>
-
     <!--SIM does not save, but the voice mail number to be changed. -->
     <bool name="editable_voicemailnumber">false</bool>
 
@@ -2617,4 +2622,16 @@
         <!-- Verizon requires any SMS that starts with //VZWVVM to be treated as a VVM SMS-->
         <item>310004,310010,310012,310013,310590,310890,310910,311110,311270,311271,311272,311273,311274,311275,311276,311277,311278,311279,311280,311281,311282,311283,311284,311285,311286,311287,311288,311289,311390,311480,311481,311482,311483,311484,311485,311486,311487,311488,311489;^//VZWVVM.*</item>
     </string-array>
+
+    <!-- This config is holding calling number conversion map - expected to convert to emergency
+         number. Formats for this config as below:
+         <item>[dialstring1],[dialstring2],[dialstring3]:[replacement]</item>
+
+         E.g. for Taiwan Type Approval, 110 and 119 should be converted to 112.
+         <item>110,119:112</item>
+    -->
+    <string-array translatable="false" name="config_convert_to_emergency_number_map" />
+
+    <!-- An array of packages for which notifications cannot be blocked. -->
+    <string-array translatable="false" name="config_nonBlockableNotificationPackages" />
 </resources>
diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml
index a139529..3a1679c 100644
--- a/core/res/res/values/donottranslate.xml
+++ b/core/res/res/values/donottranslate.xml
@@ -26,4 +26,7 @@
     <string name="icu_abbrev_wday_month_day_no_year">eeeMMMMd</string>
     <!-- @hide DO NOT TRANSLATE. date formatting pattern for system ui.-->
     <string name="system_ui_date_pattern">@string/icu_abbrev_wday_month_day_no_year</string>
+    <!-- @hide DO NOT TRANSLATE Spans within this text are applied to style composing regions
+    within an EditText widget. The text content is ignored and not used. -->
+    <string name="candidates_style" translatable="false"><u>candidates</u></string>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 40b6047..f1118d7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3130,8 +3130,6 @@
     <string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
     <string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
 
-    <string name="candidates_style"><u>candidates</u></string>
-
     <!-- External media notification strings -->
     <skip />
 
@@ -4346,6 +4344,8 @@
         <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
     </plurals>
 
+    <string name="default_notification_channel_label">Miscellaneous</string>
+
     <string name="importance_from_user">You set the importance of these notifications.</string>
     <string name="importance_from_person">This is important because of the people involved.</string>
 
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 224e3b7..22bdf7e 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -553,11 +553,7 @@
         <item name="textOff">@string/capital_off</item>
     </style>
 
-    <style name="Widget.Material.BaseButtonBar">
-        <item name="background">?attr/colorBackgroundFloating</item>
-    </style>
-
-    <style name="Widget.Material.ButtonBar" parent="Widget.Material.BaseButtonBar">
+    <style name="Widget.Material.ButtonBar">
         <item name="background">@null</item>
     </style>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 647fc57..cf6fdc8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1754,6 +1754,7 @@
   <java-symbol type="integer" name="config_networkTransitionTimeout" />
   <java-symbol type="integer" name="config_networkNotifySwitchType" />
   <java-symbol type="array" name="config_networkNotifySwitches" />
+  <java-symbol type="integer" name="config_networkAvoidBadWifi" />
   <java-symbol type="integer" name="config_notificationsBatteryFullARGB" />
   <java-symbol type="integer" name="config_notificationsBatteryLedOff" />
   <java-symbol type="integer" name="config_notificationsBatteryLedOn" />
@@ -2265,8 +2266,8 @@
   <java-symbol type="bool" name="config_auto_attach_data_on_creation" />
   <java-symbol type="attr" name="closeItemLayout" />
   <java-symbol type="layout" name="resolver_different_item_header" />
-  <java-symbol type="array" name="config_default_vm_number" />
   <java-symbol type="integer" name="config_cdma_3waycall_flash_delay"/>
+  <java-symbol type="array" name="config_default_vm_number" />
   <java-symbol type="attr" name="windowBackgroundFallback" />
   <java-symbol type="id" name="textSpacerNoButtons" />
   <java-symbol type="array" name="dial_string_replace" />
@@ -2469,6 +2470,7 @@
   <java-symbol type="dimen" name="notification_content_margin_top" />
   <java-symbol type="dimen" name="notification_content_margin_bottom" />
   <java-symbol type="dimen" name="notification_header_background_height" />
+  <java-symbol type="string" name="default_notification_channel_label" />
   <java-symbol type="string" name="importance_from_user" />
   <java-symbol type="string" name="importance_from_person" />
 
@@ -2714,4 +2716,7 @@
 
   <java-symbol type="drawable" name="ic_restart" />
 
+  <java-symbol type="array" name="config_convert_to_emergency_number_map" />
+
+  <java-symbol type="array" name="config_nonBlockableNotificationPackages" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 2452cfd..cef4784 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -479,6 +479,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.view.ScaleGesture" android:label="ScaleGesture">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.StubbedView" android:label="ViewStub">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/res/layout/scale_gesture.xml b/core/tests/coretests/res/layout/scale_gesture.xml
new file mode 100644
index 0000000..05d408d
--- /dev/null
+++ b/core/tests/coretests/res/layout/scale_gesture.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<!-- Demonstrates use of scale gesture detector to perform pinch to zoom.
+     See corresponding Java code. -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!"
+        android:id="@+id/article"
+        android:textSize="12sp"/>
+
+</RelativeLayout>
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
index 582bf1a..985fa4f 100644
--- a/core/tests/coretests/src/android/os/OsTests.java
+++ b/core/tests/coretests/src/android/os/OsTests.java
@@ -32,6 +32,7 @@
         suite.addTestSuite(IdleHandlerTest.class);
         suite.addTestSuite(MessageQueueTest.class);
         suite.addTestSuite(MessengerTest.class);
+        suite.addTestSuite(PatternMatcherTest.class);
         suite.addTestSuite(SystemPropertiesTest.class);
 
         return suite;
diff --git a/core/tests/coretests/src/android/os/PatternMatcherTest.java b/core/tests/coretests/src/android/os/PatternMatcherTest.java
new file mode 100644
index 0000000..9645ccc
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PatternMatcherTest.java
@@ -0,0 +1,234 @@
+package android.os;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class PatternMatcherTest extends TestCase{
+
+    @Test
+    public void testAdvancedPatternMatchesAnyToken() {
+        PatternMatcher matcher = new PatternMatcher(".", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a", matcher);
+        assertMatches("b", matcher);
+        assertNotMatches("", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesSetToken() {
+        PatternMatcher matcher = new PatternMatcher("[a]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a", matcher);
+        assertNotMatches("b", matcher);
+
+        matcher = new PatternMatcher("[.*+{}\\]\\\\[]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches(".", matcher);
+        assertMatches("*", matcher);
+        assertMatches("+", matcher);
+        assertMatches("{", matcher);
+        assertMatches("}", matcher);
+        assertMatches("]", matcher);
+        assertMatches("\\", matcher);
+        assertMatches("[", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesSetCharacterClassToken() {
+        PatternMatcher matcher = new PatternMatcher("[a-z]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a", matcher);
+        assertMatches("b", matcher);
+        assertNotMatches("A", matcher);
+        assertNotMatches("1", matcher);
+
+        matcher = new PatternMatcher("[a-z][0-9]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a1", matcher);
+        assertNotMatches("1a", matcher);
+        assertNotMatches("aa", matcher);
+
+        matcher = new PatternMatcher("[z-a]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertNotMatches("a", matcher);
+        assertNotMatches("z", matcher);
+        assertNotMatches("A", matcher);
+
+        matcher = new PatternMatcher("[^0-9]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a", matcher);
+        assertMatches("z", matcher);
+        assertMatches("A", matcher);
+        assertNotMatches("9", matcher);
+        assertNotMatches("5", matcher);
+        assertNotMatches("0", matcher);
+
+        assertPoorlyFormattedPattern("[]a]");
+        matcher = new PatternMatcher("[\\[a]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a", matcher);
+        assertMatches("[", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesEscapedCharacters() {
+        PatternMatcher matcher = new PatternMatcher("\\.", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches(".", matcher);
+        assertNotMatches("a", matcher);
+        assertNotMatches("1", matcher);
+
+        matcher = new PatternMatcher("a\\+", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a+", matcher);
+        assertNotMatches("a", matcher);
+        assertNotMatches("aaaaa", matcher);
+
+        matcher = new PatternMatcher("[\\a-\\z]", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertMatches("a", matcher);
+        assertMatches("z", matcher);
+        assertNotMatches("A", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesLiteralTokens() {
+        PatternMatcher matcher = new PatternMatcher("a", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertNotMatches("", matcher);
+        assertMatches("a", matcher);
+        assertNotMatches("z", matcher);
+
+        matcher = new PatternMatcher("az", PatternMatcher.PATTERN_ADVANCED_GLOB);
+        assertNotMatches("", matcher);
+        assertMatches("az", matcher);
+        assertNotMatches("za", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesSetZeroOrMore() {
+        PatternMatcher matcher = new PatternMatcher("[a-z]*", PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertMatches("", matcher);
+        assertMatches("a", matcher);
+        assertMatches("abcdefg", matcher);
+        assertNotMatches("abc1", matcher);
+        assertNotMatches("1abc", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesSetOneOrMore() {
+        PatternMatcher matcher = new PatternMatcher("[a-z]+", PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertNotMatches("", matcher);
+        assertMatches("a", matcher);
+        assertMatches("abcdefg", matcher);
+        assertNotMatches("abc1", matcher);
+        assertNotMatches("1abc", matcher);
+    }
+
+
+    @Test
+    public void testAdvancedPatternMatchesSingleRange() {
+        PatternMatcher matcher = new PatternMatcher("[a-z]{1}",
+                PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertNotMatches("", matcher);
+        assertMatches("a", matcher);
+        assertMatches("z", matcher);
+        assertNotMatches("1", matcher);
+        assertNotMatches("aa", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesFullRange() {
+        PatternMatcher matcher = new PatternMatcher("[a-z]{1,5}",
+                PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertNotMatches("", matcher);
+        assertMatches("a", matcher);
+        assertMatches("zazaz", matcher);
+        assertNotMatches("azazaz", matcher);
+        assertNotMatches("11111", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesPartialRange() {
+        PatternMatcher matcher = new PatternMatcher("[a-z]{3,}",
+                PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertNotMatches("", matcher);
+        assertMatches("aza", matcher);
+        assertMatches("zazaz", matcher);
+        assertMatches("azazazazazaz", matcher);
+        assertNotMatches("aa", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternMatchesComplexPatterns() {
+        PatternMatcher matcher = new PatternMatcher(
+                "/[0-9]{4}/[0-9]{2}/[0-9]{2}/[a-zA-Z0-9_]+\\.html",
+                PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertNotMatches("", matcher);
+        assertMatches("/2016/09/07/got_this_working.html", matcher);
+        assertMatches("/2016/09/07/got_this_working2.html", matcher);
+        assertNotMatches("/2016/09/07/got_this_working2dothtml", matcher);
+        assertNotMatches("/2016/9/7/got_this_working.html", matcher);
+
+        matcher = new PatternMatcher(
+                "/b*a*bar.*",
+                PatternMatcher.PATTERN_ADVANCED_GLOB);
+
+        assertMatches("/babar", matcher);
+        assertMatches("/babarfff", matcher);
+        assertMatches("/bbaabarfff", matcher);
+        assertMatches("/babar?blah", matcher);
+        assertMatches("/baaaabar?blah", matcher);
+        assertNotMatches("?bar", matcher);
+        assertNotMatches("/bar", matcher);
+        assertNotMatches("/baz", matcher);
+        assertNotMatches("/ba/bar", matcher);
+        assertNotMatches("/barf", matcher);
+        assertNotMatches("/", matcher);
+        assertNotMatches("?blah", matcher);
+    }
+
+    @Test
+    public void testAdvancedPatternPoorFormatThrowsIllegalArgumentException() {
+        assertPoorlyFormattedPattern("[a-z");
+        assertPoorlyFormattedPattern("a{,4}");
+        assertPoorlyFormattedPattern("a{0,a}");
+        assertPoorlyFormattedPattern("a{\\1, 2}");
+        assertPoorlyFormattedPattern("[]");
+        assertPoorlyFormattedPattern("a{}");
+        assertPoorlyFormattedPattern("{3,4}");
+        assertPoorlyFormattedPattern("a+{3,4}");
+        assertPoorlyFormattedPattern("*.");
+        assertPoorlyFormattedPattern(".+*");
+        assertPoorlyFormattedPattern("a{3,4");
+        assertPoorlyFormattedPattern("[a");
+        assertPoorlyFormattedPattern("abc\\");
+        assertPoorlyFormattedPattern("+.");
+
+        StringBuilder charSet = new StringBuilder("[");
+        for (int i = 0; i < 1024; i++) {
+            charSet.append('a' + (i % 26));
+        }
+        charSet.append("]");
+        assertPoorlyFormattedPattern(charSet.toString());
+    }
+
+    private void assertMatches(String string, PatternMatcher matcher) {
+        assertTrue("'" + string + "' should match '" + matcher.toString() + "'",
+                matcher.match(string));
+    }
+
+    private void assertNotMatches(String string, PatternMatcher matcher) {
+        assertTrue("'" + string + "' should not match '" + matcher.toString() + "'",
+                !matcher.match(string));
+    }
+
+    private void assertPoorlyFormattedPattern(String format) {
+        try {
+            new PatternMatcher(format, PatternMatcher.PATTERN_ADVANCED_GLOB);
+        } catch (IllegalArgumentException e) {
+            return;// expected
+        }
+
+        fail("'" + format + "' was erroneously created");
+    }
+}
diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java
new file mode 100644
index 0000000..78a4b31
--- /dev/null
+++ b/core/tests/coretests/src/android/view/PinchZoomAction.java
@@ -0,0 +1,250 @@
+/*
+ * 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.view;
+
+import static android.support.test.espresso.core.deps.guava.base.Preconditions.checkNotNull;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static org.hamcrest.Matchers.allOf;
+
+import android.os.SystemClock;
+import android.support.test.espresso.InjectEventSecurityException;
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.MotionEvents;
+import android.support.test.espresso.action.Swiper;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.util.HumanReadables;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import javax.annotation.Nullable;
+import org.hamcrest.Matcher;
+
+/**
+ * Pinch and zooms on a View using touch events.
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be displayed on screen
+ * <ul>
+ */
+public class PinchZoomAction implements ViewAction {
+    public static Swiper.Status sendPinchZoomAction(UiController uiController,
+                                                    float[] firstFingerStartCoords,
+                                                    float[] firstFingerEndCoords,
+                                                    float[] secondFingerStartCoords,
+                                                    float[] secondFingerEndCoords,
+                                                    float[] precision) {
+        checkNotNull(uiController);
+        checkNotNull(firstFingerStartCoords);
+        checkNotNull(firstFingerEndCoords);
+        checkNotNull(secondFingerStartCoords);
+        checkNotNull(secondFingerEndCoords);
+        checkNotNull(precision);
+
+        // Specify the touch properties for the finger events.
+        final MotionEvent.PointerProperties pp1 = new MotionEvent.PointerProperties();
+        pp1.id = 0;
+        pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
+        final MotionEvent.PointerProperties pp2 = new MotionEvent.PointerProperties();
+        pp2.id = 1;
+        pp2.toolType = MotionEvent.TOOL_TYPE_FINGER;
+        MotionEvent.PointerProperties[] pointerProperties =
+                new MotionEvent.PointerProperties[]{pp1, pp2};
+
+        // Specify the motion properties of the two touch points.
+        final MotionEvent.PointerCoords pc1 = new MotionEvent.PointerCoords();
+        pc1.x = firstFingerStartCoords[0];
+        pc1.y = firstFingerStartCoords[1];
+        pc1.pressure = 1;
+        pc1.size = 1;
+        final MotionEvent.PointerCoords pc2 = new MotionEvent.PointerCoords();
+        pc2.x = secondFingerStartCoords[0];
+        pc2.y = secondFingerEndCoords[1];
+        pc2.pressure = 1;
+        pc2.size = 1;
+
+        final long startTime = SystemClock.uptimeMillis();
+        long eventTime = startTime;
+        final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[]{pc1, pc2};
+
+        final MotionEvent firstFingerEvent = MotionEvent.obtain(startTime,
+                eventTime, MotionEvent.ACTION_DOWN, 1, pointerProperties, pointerCoords,
+                0, 0, 1, 1, 0, 0, 0, 0);
+
+        eventTime = SystemClock.uptimeMillis();
+        final MotionEvent secondFingerEvent = MotionEvent.obtain(startTime, eventTime,
+                MotionEvent.ACTION_POINTER_DOWN +
+                        (pp2.id << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+                2, pointerProperties, pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0);
+
+        try {
+            uiController.injectMotionEvent(firstFingerEvent);
+        } catch (InjectEventSecurityException e) {
+            throw new PerformException.Builder()
+                    .withActionDescription("First finger down event")
+                    .withViewDescription("Scale gesture detector")
+                    .withCause(e)
+                    .build();
+        }
+
+        try {
+            uiController.injectMotionEvent(secondFingerEvent);
+        } catch (InjectEventSecurityException e) {
+            throw new PerformException.Builder()
+                    .withActionDescription("Second finger down event")
+                    .withViewDescription("Scale gesture detector")
+                    .withCause(e)
+                    .build();
+        }
+
+        // Specify the coordinates of the two touch points.
+        final float[][] stepsFirstFinger = interpolate(firstFingerStartCoords,
+                firstFingerEndCoords);
+        final float[][] stepsSecondFinger = interpolate(secondFingerStartCoords,
+                secondFingerEndCoords);
+
+        // Loop until the end points of the two fingers are reached.
+        for (int i = 0; i < PINCH_STEP_COUNT; i++) {
+            eventTime = SystemClock.uptimeMillis();
+
+            pc1.x = stepsFirstFinger[i][0];
+            pc1.y = stepsFirstFinger[i][1];
+            pc2.x = stepsSecondFinger[i][0];
+            pc2.y = stepsSecondFinger[i][1];
+
+            final MotionEvent event = MotionEvent.obtain(startTime, eventTime,
+                    MotionEvent.ACTION_MOVE, 2, pointerProperties, pointerCoords,
+                    0, 0, 1, 1, 0, 0, 0, 0);
+
+            try {
+                uiController.injectMotionEvent(event);
+            } catch (InjectEventSecurityException e) {
+                throw new PerformException.Builder()
+                        .withActionDescription("Move event")
+                        .withViewDescription("Scale gesture event")
+                        .withCause(e)
+                        .build();
+            }
+
+           uiController.loopMainThreadForAtLeast(800);
+        }
+
+        eventTime = SystemClock.uptimeMillis();
+
+        // Send the up event for the second finger.
+        final MotionEvent secondFingerUpEvent = MotionEvent.obtain(startTime, eventTime,
+                MotionEvent.ACTION_POINTER_UP, 2, pointerProperties, pointerCoords,
+                0, 0, 1, 1, 0, 0, 0, 0);
+        try {
+            uiController.injectMotionEvent(secondFingerUpEvent);
+        } catch (InjectEventSecurityException e) {
+            throw new PerformException.Builder()
+                    .withActionDescription("Second finger up event")
+                    .withViewDescription("Scale gesture detector")
+                    .withCause(e)
+                    .build();
+        }
+
+        eventTime = SystemClock.uptimeMillis();
+        // Send the up event for the first finger.
+        final MotionEvent firstFingerUpEvent = MotionEvent.obtain(startTime, eventTime,
+                MotionEvent.ACTION_POINTER_UP, 1, pointerProperties, pointerCoords,
+                0, 0, 1, 1, 0, 0, 0, 0);
+        try {
+            uiController.injectMotionEvent(firstFingerUpEvent);
+        } catch (InjectEventSecurityException e) {
+            throw new PerformException.Builder()
+                    .withActionDescription("First finger up event")
+                    .withViewDescription("Scale gesture detector")
+                    .withCause(e)
+                    .build();
+        }
+        return Swiper.Status.SUCCESS;
+    }
+
+    private static float[][] interpolate(float[] start, float[] end) {
+        float[][] res = new float[PINCH_STEP_COUNT][2];
+
+        for (int i = 0; i < PINCH_STEP_COUNT; i++) {
+            res[i][0] = start[0] + (end[0] - start[0]) * i / (PINCH_STEP_COUNT - 1f);
+            res[i][1] = start[1] + (end[1] - start[1]) * i / (PINCH_STEP_COUNT - 1f);
+        }
+
+        return res;
+    }
+
+    /** The number of move events to send for each pinch. */
+    private static final int PINCH_STEP_COUNT = 10;
+
+    private final Class<? extends View> mViewClass;
+    private final float[] mFirstFingerStartCoords;
+    private final float[] mFirstFingerEndCoords;
+    private final float[] mSecondFingerStartCoords;
+    private final float[] mSecondFingerEndCoords;
+
+    public PinchZoomAction(float[] firstFingerStartCoords,
+                           float[] firstFingerEndCoords,
+                           float[] secondFingerStartCoords,
+                           float[] secondFingerEndCoords,
+                           Class<? extends View> viewClass) {
+        mFirstFingerStartCoords = firstFingerStartCoords;
+        mFirstFingerEndCoords = firstFingerEndCoords;
+        mSecondFingerStartCoords = secondFingerStartCoords;
+        mSecondFingerEndCoords = secondFingerEndCoords;
+        mViewClass = viewClass;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Matcher<View> getConstraints() {
+        return allOf(isCompletelyDisplayed(), isAssignableFrom(mViewClass));
+    }
+
+    @Override
+    public void perform(UiController uiController, View view) {
+        checkNotNull(uiController);
+        checkNotNull(view);
+        Swiper.Status status;
+        final float[] precision = {1.0f, 1.0f, 1.0f, 1.0f};
+
+        try {
+            status = sendPinchZoomAction(uiController, this.mFirstFingerStartCoords,
+                this.mFirstFingerEndCoords, this.mSecondFingerStartCoords,
+                this.mSecondFingerEndCoords, precision);
+        } catch (RuntimeException re) {
+            throw new PerformException.Builder()
+                    .withActionDescription(getDescription())
+                    .withViewDescription(HumanReadables.describe(view))
+                    .withCause(re)
+                    .build();
+        }
+        if (status == Swiper.Status.FAILURE) {
+            throw new PerformException.Builder()
+                    .withActionDescription(getDescription())
+                    .withViewDescription(HumanReadables.describe(view))
+                    .withCause(new RuntimeException(getDescription() + " failed"))
+                    .build();
+        }
+    }
+
+    @Override
+    public String getDescription() {
+        return "Pinch Zoom Action";
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ScaleGesture.java b/core/tests/coretests/src/android/view/ScaleGesture.java
new file mode 100644
index 0000000..a954a4a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ScaleGesture.java
@@ -0,0 +1,58 @@
+/*
+* 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.view;
+
+import com.android.frameworks.coretests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
+import android.widget.TextView;
+
+public class ScaleGesture extends Activity {
+    private ScaleGestureDetector mScaleGestureDetector;
+    private float mFactor;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.scale_gesture);
+        mScaleGestureDetector = new ScaleGestureDetector(this, new OnScaleGestureListener());
+        mFactor = 1.0f;
+    }
+
+    public float getScaleFactor() {
+        return mFactor;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        mScaleGestureDetector.onTouchEvent(event);
+        return true;
+    }
+
+    public class OnScaleGestureListener extends SimpleOnScaleGestureListener {
+        @Override
+        public boolean onScale(ScaleGestureDetector detector) {
+            mFactor *= detector.getScaleFactor();
+            return true;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
new file mode 100644
index 0000000..23d0251
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -0,0 +1,91 @@
+/*
+* Copyright (C) 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.view;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.DisplayMetrics;
+import android.view.PinchZoomAction;
+import android.view.ScaleGesture;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.Espresso.onView;
+
+public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> {
+    private ScaleGesture mScaleGestureActivity;
+
+    public ScaleGestureDetectorTest() {
+        super("com.android.frameworks.coretests", ScaleGesture.class);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+        mScaleGestureActivity = getActivity();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testScaleGestureDetector() {
+        // No scaling should have occurred prior to performing pinch zoom action.
+        final float initialScaleFactor = 1.0f;
+        assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor());
+
+        // Specify start and end coordinates, irrespective of device display size.
+        final DisplayMetrics dm = new DisplayMetrics();
+        final WindowManager wm = (WindowManager) (mScaleGestureActivity.getApplicationContext())
+                .getSystemService(Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getMetrics(dm);
+        final int displayWidth = dm.widthPixels;
+        final int displayHeight = dm.heightPixels;
+
+        // Obtain coordinates to perform pinch and zoom from the center, to 75% of the display.
+        final int centerX = displayWidth / 2;
+        final int centerY = displayHeight / 2;
+
+        // Offset center coordinates by one, so that the two starting points are different.
+        final float[] firstFingerStartCoords = new float[] {centerX + 1.0f, centerY - 1.0f};
+        final float[] firstFingerEndCoords =
+        new float[] {0.75f * displayWidth, 0.25f * displayHeight};
+        final float[] secondFingerStartCoords = new float[] {centerX - 1.0f, centerY + 1.0f};
+        final float[] secondFingerEndCoords =
+        new float[] {0.25f * displayWidth, 0.75f * displayHeight};
+
+        onView(withId(R.id.article)).perform(new PinchZoomAction(firstFingerStartCoords,
+                firstFingerEndCoords, secondFingerStartCoords, secondFingerEndCoords,
+                TextView.class));
+
+        // Text should have been 'zoomed', meaning scale factor increased.
+        assertTrue(mScaleGestureActivity.getScaleFactor() > initialScaleFactor);
+    }
+}
\ No newline at end of file
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 117e17b..345ec37 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -350,7 +350,7 @@
     <family lang="und-Zsye">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
-    <family>
+    <family lang="und-Zsym">
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
     </family>
     <family>
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index f5d23e8..2721c85 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -59,7 +59,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2016.
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 5, 2016.
 <br/>Any versions with less than 0.1% distribution are not shown.</em>
 </p>
 
@@ -81,7 +81,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2016.
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 5, 2016.
 
 <br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
 
@@ -101,7 +101,7 @@
 
 
 <img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A46.0%2C42.6%2C11.4&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A44.9%2C42.3%2C12.8&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
 
 <p>To declare which version of OpenGL ES your application requires, you should use the {@code
 android:glEsVersion} attribute of the <a
@@ -119,21 +119,21 @@
 </tr>
 <tr>
 <td>2.0</td>
-<td>46.0%</td>
+<td>44.9%</td>
 </tr>
 <tr>
 <td>3.0</td>
-<td>42.6%</td>
+<td>42.3%</td>
 </tr>
 <tr>
 <td>3.1</td>
-<td>11.4%</td>
+<td>12.8%</td>
 </tr>
 </table>
 
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2016</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 5, 2016</em></p>
 
 
 
@@ -147,19 +147,19 @@
       "Large": {
         "hdpi": "0.5",
         "ldpi": "0.2",
-        "mdpi": "4.3",
+        "mdpi": "4.1",
         "tvdpi": "2.1",
         "xhdpi": "0.5"
       },
       "Normal": {
-        "hdpi": "40.0",
-        "mdpi": "3.8",
-        "tvdpi": "0.1",
-        "xhdpi": "27.3",
+        "hdpi": "39.5",
+        "mdpi": "3.5",
+        "tvdpi": "0.2",
+        "xhdpi": "28.4",
         "xxhdpi": "15.5"
       },
       "Small": {
-        "ldpi": "1.8"
+        "ldpi": "1.6"
       },
       "Xlarge": {
         "hdpi": "0.3",
@@ -167,8 +167,8 @@
         "xhdpi": "0.7"
       }
     },
-    "densitychart": "//chart.googleapis.com/chart?chd=t%3A2.0%2C11.0%2C2.2%2C40.8%2C28.5%2C15.5&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&cht=p&chs=400x250&chco=c4df9b%2C6fad0c",
-    "layoutchart": "//chart.googleapis.com/chart?chd=t%3A3.9%2C7.6%2C86.7%2C1.8&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall&cht=p&chs=400x250&chco=c4df9b%2C6fad0c"
+    "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chd=t%3A1.8%2C10.5%2C2.3%2C40.4%2C29.6%2C15.5&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250",
+    "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A3.9%2C7.4%2C87.2%2C1.6&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250"
   }
 ];
 
@@ -176,7 +176,7 @@
 var VERSION_DATA =
 [
   {
-    "chart": "//chart.googleapis.com/chart?chd=t%3A0.1%2C1.7%2C1.6%2C16.7%2C29.2%2C35.5%2C15.2&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop%7CMarshmallow&cht=p&chs=500x250&chco=c4df9b%2C6fad0c",
+    "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop%7CMarshmallow&chd=t%3A0.1%2C1.5%2C1.4%2C15.6%2C27.7%2C35.0%2C18.7&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=500x250",
     "data": [
       {
         "api": 8,
@@ -186,47 +186,47 @@
       {
         "api": 10,
         "name": "Gingerbread",
-        "perc": "1.7"
+        "perc": "1.5"
       },
       {
         "api": 15,
         "name": "Ice Cream Sandwich",
-        "perc": "1.6"
+        "perc": "1.4"
       },
       {
         "api": 16,
         "name": "Jelly Bean",
-        "perc": "6.0"
+        "perc": "5.6"
       },
       {
         "api": 17,
         "name": "Jelly Bean",
-        "perc": "8.3"
+        "perc": "7.7"
       },
       {
         "api": 18,
         "name": "Jelly Bean",
-        "perc": "2.4"
+        "perc": "2.3"
       },
       {
         "api": 19,
         "name": "KitKat",
-        "perc": "29.2"
+        "perc": "27.7"
       },
       {
         "api": 21,
         "name": "Lollipop",
-        "perc": "14.1"
+        "perc": "13.1"
       },
       {
         "api": 22,
         "name": "Lollipop",
-        "perc": "21.4"
+        "perc": "21.9"
       },
       {
         "api": 23,
         "name": "Marshmallow",
-        "perc": "15.2"
+        "perc": "18.7"
       }
     ]
   }
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index bd933f4..cee8327 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -14,24 +14,26 @@
   <div class="wrap">
     <div class="cols dac-hero-content">
       <div class="col-1of2 col-push-1of2 dac-hero-figure">
-        <img class="dac-hero-image" src="/images/develop/hero_image_studio5_2x.png" srcset="/images/develop/hero_image_studio5.png 1x, /images/develop/hero_image_studio5_2x.png 2x">
+        <img class="dac-hero-image" style="padding-top:32px"
+          src="/images/develop/hero-layout-editor_2x.png">
       </div>
       <div class="col-1of2 col-pull-1of2" style="margin-bottom:40px">
         <h1 class="dac-hero-title">
             <a style="color:inherit" href="{@docRoot}studio/index.html">
-            Android Studio 2.1,<br>now available!</a></h1>
+            Android Studio 2.2 <nobr>is here!</nobr></a></h1>
 
-<p class="dac-hero-description">Android Studio provides the fastest tools for
-building apps on every type of Android device.</p>
+<p class="dac-hero-description">The latest version of Android Studio includes a
+rewritten <b>layout editor</b> with the new constraint layout,
+helping you build rich UI with less work.</p>
 
-<p class="dac-hero-description">The latest version, Android Studio 2.1, adds
-support for N Preview development on top of the faster Android Emulator and
-Instant Run feature from 2.0.</p>
+
+<p class="dac-hero-description">With over a dozen new features, Android Studio
+2.2 helps you code faster and smarter.</p>
 
 <p style="margin-top:24px">
     <a class="dac-hero-cta" href="{@docRoot}studio/index.html">
       <span class="dac-sprite dac-auto-chevron"></span>
-      Get Android Studio
+      Get Android Studio 2.2
     </a>
   &nbsp;&nbsp;&nbsp;&nbsp;<wbr>
     <a class="dac-hero-cta" href="{@docRoot}studio/releases/index.html">
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index 292cfcc..ad09f1f 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -235,7 +235,7 @@
 
 <p>"<em>product_id</em>","<em>publish_state</em>","<em>purchase_type</em>","<em>autotranslate</em>
 ","<em>locale</em>; <em>title</em>; <em>description</em>","<em>autofill</em>","<em>country</em>;
-<em>price</em>"
+<em>price</em>", "<em>pricing_template_id</em>"
 </p>
 
 <p>Descriptions and usage details are provided below.</p>
@@ -316,8 +316,9 @@
         <p>"true","<em>default_price_in_home_currency</em>"
       </li>
       <li>
-        <p>If <em>autofill</em> is set to <code>false</code>, you need to specify a <em>country</em>
-        and a <em>price</em> for each currency, and you must use the following syntax:</p>
+        <p>If <em>autofill</em> is set to <code>false</code>, you need to either specify the <em>pricing_template_id</em>
+        that is linked to the in-app product or specify a <em>country</em> and a <em>price</em> for each currency.
+        If you choose to specify countries and prices, you must use the following syntax:</p>
         <p>"false", "<em>home_country</em>; <em>default_price_in_home_currency</em>; <em>country_2</em>;
         <em>country_2_price</em>; <em>country_3</em>; <em>country_3_price</em>; ..."</p>
       </li>
@@ -335,11 +336,41 @@
   </dd>
   <dt>price</dt>
   <dd>
+    <p>
+    If you use this value, you shouldn't specify a value for the <em>pricing_template_id</em>.
+    </p>
+    <p>
     This is equivalent to the Price in the In-app Products UI. The price must be specified in
     micro-units. To convert a currency value to micro-units, you multiply the real value by
     1,000,000.
     For example, if you want to sell an in-app item for $1.99, you specify <code>1990000</code> in the
     <em>price</em> field.
+    </p>
+  </dd>
+  <dt>pricing_template_id</dt>
+  <dd>
+  <p>
+    If you use this value, you should set <em>autofill</em> to
+    <code>false</code> and leave the <em>price</em> column empty.
+  </p>
+  <p>
+    This value represents the ID of the pricing template that you've linked to
+    the in-app product. This ID appears under a pricing template's name
+    on the <strong>Pricing template</strong> page. If an in-app product isn't
+    linked to a pricing template, its <em>pricing_template_id</em> value is
+    empty.
+  </p>
+  <p>
+    If you import a CSV file and choose to overwrite the product list, you can
+    update the links between in-app products and pricing templates by changing
+    the value of an in-app product's <em>pricing_template_id</em>. Leave the
+    value empty to unlink an in-app product from all pricing templates.
+  </p>
+  <p>
+    <strong>Note: </strong>You can link up to 100 app prices or in-app product
+    prices with a particular pricing template. Therefore, don't specify the same
+    <em>pricing_template_id</em> value in more than 100 rows of your CSV file.
+  </p>
   </dd>
 </dl>
 
@@ -432,8 +463,11 @@
 
 <p>
   When creating a pricing template, you provide new pricing information that you
-  can apply to paid apps and in-app products. To add a pricing template, do the
-  following:
+  can apply to paid apps and in-app products. You can link the prices of up to
+  100 apps and in-app products to a single pricing template.
+</p>
+</p>
+  To add a pricing template, do the following:
 </p>
 
 <ol>
diff --git a/docs/html/guide/topics/location/strategies.jd b/docs/html/guide/topics/location/strategies.jd
index 2dfed2c..548ed9c 100755
--- a/docs/html/guide/topics/location/strategies.jd
+++ b/docs/html/guide/topics/location/strategies.jd
@@ -133,36 +133,69 @@
 both to zero requests location notifications as frequently as possible. The last parameter is your
 {@link android.location.LocationListener}, which receives callbacks for location updates.</p>
 
-<p>To request location updates from the GPS provider,
-substitute <code>GPS_PROVIDER</code> for <code>NETWORK_PROVIDER</code>. You can also request
-location updates from both the GPS and the Network Location Provider by calling {@link
-android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} twice&mdash;once
-for <code>NETWORK_PROVIDER</code> and once for <code>GPS_PROVIDER</code>.</p>
+<p>To request location updates from the GPS provider, use {@link
+android.location.LocationManager#GPS_PROVIDER} instead of {@link
+android.location.LocationManager#NETWORK_PROVIDER}. You can also request
+location updates from both the GPS and the Network Location Provider by calling
+{@link android.location.LocationManager#requestLocationUpdates
+requestLocationUpdates()} twice&mdash;once for {@link
+android.location.LocationManager#NETWORK_PROVIDER} and once for {@link
+android.location.LocationManager#GPS_PROVIDER}.</p>
 
 
 <h3 id="Permission">Requesting User Permissions</h3>
 
-<p>In order to receive location updates from <code>NETWORK_PROVIDER</code> or
-<code>GPS_PROVIDER</code>, you must request user permission by declaring either the {@code
-ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permission, respectively, in your Android
-manifest file. For example:</p>
+<p>
+  In order to receive location updates from {@link
+  android.location.LocationManager#NETWORK_PROVIDER} or {@link
+  android.location.LocationManager#GPS_PROVIDER}, you must request the user's
+  permission by declaring either the {@code ACCESS_COARSE_LOCATION} or {@code
+  ACCESS_FINE_LOCATION} permission, respectively, in your Android manifest file.
+  Without these permissions, your application will fail at runtime when
+  requesting location updates.
+</p>
 
+<p>
+  If you are using both {@link
+  android.location.LocationManager#NETWORK_PROVIDER} and {@link
+  android.location.LocationManager#GPS_PROVIDER}, then you need to request only
+  the {@code ACCESS_FINE_LOCATION} permission, because it includes permission
+  for both providers. Permission for {@code ACCESS_COARSE_LOCATION} allows
+  access only to {@link android.location.LocationManager#NETWORK_PROVIDER}.
+</p>
+
+<p id="location-feature-caution" class="caution">
+  <strong>Caution:</strong> If your app targets Android 5.0 (API level 21) or
+  higher, you <em>must</em> declare that your app uses the
+  <code>android.hardware.location.network</code> or
+  <code>android.hardware.location.gps</code> hardware feature in the manifest
+  file, depending on whether your app receives location updates from {@link
+  android.location.LocationManager#NETWORK_PROVIDER} or from {@link
+  android.location.LocationManager#GPS_PROVIDER}. If your app receives location
+  information from either of these location provider sources, you need to
+  declare that the app uses these hardware features in your app manifest.
+  On devices running verions prior to Android 5.0 (API 21), requesting the
+  {@code ACCESS_FINE_LOCATION} or {@code ACCESS_COARSE_LOCATION} permission
+  includes an implied request for location hardware features. However,
+  requesting those permissions <em>does not</em> automatically request location
+  hardware features on Android 5.0 (API level 21) and higher.
+</p>
+
+<p>
+   The following code sample demonstrates how to declare the permission and
+   hardware feature in the manifest file of an app that reads data from the
+   device's GPS:
+</p>
 <pre>
 &lt;manifest ... &gt;
     &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt;
     ...
+    &lt;!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --&gt;
+    &lt;uses-feature android:name="android.hardware.location.gps" /&gt;
+    ...
 &lt;/manifest&gt;
 </pre>
 
-<p>Without these permissions, your application will fail at runtime when requesting
-location updates.</p>
-
-<p class="note"><strong>Note:</strong> If you are using both <code>NETWORK_PROVIDER</code> and
-<code>GPS_PROVIDER</code>, then you need to request only the {@code ACCESS_FINE_LOCATION}
-permission, because it includes permission for both providers. (Permission for {@code
-ACCESS_COARSE_LOCATION} includes permission only for <code>NETWORK_PROVIDER</code>.)</p>
-
-
 <h2 id="BestPerformance">Defining a Model for the Best Performance</h2>
 
   <p>Location-based applications are now commonplace, but due to the less than optimal
@@ -404,9 +437,10 @@
 the Android emulator. There are three different ways to send your application mock location
 data: using Android Studio, DDMS, or the "geo" command in the emulator console.</p>
 
-<p class="note"><strong>Note:</strong> Providing mock location data is injected as GPS location
-data, so you must request location updates from <code>GPS_PROVIDER</code> in order for mock location
-data to work.</p>
+<p class="note"><strong>Note:</strong> Providing mock location data is injected
+as GPS location data, so you must request location updates from {@link
+android.location.LocationManager#GPS_PROVIDER} in order for mock location data
+to work.</p>
 
 <h3 id="MockAVD">Using Android Studio</h3>
 
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 9b32244..26ae59f 100755
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -1666,6 +1666,15 @@
 
 <pre>&lt;uses-feature android:name="android.hardware.camera" android:required="false" /&gt;</pre>
 
+<p class="caution">
+  <strong>Caution:</strong> If your app targets Android 5.0 (API level 21) or
+  higher and uses the <code>ACCESS_COARSE_LOCATION</code> or
+  <code>ACCESS_FINE_LOCATION</code> permission in order to receive location
+  updates from the network or a GPS, respectively, you must also explicitly
+  declare that your app uses the <code>android.hardware.location.network</code>
+  or <code>android.hardware.location.gps</code> hardware features.
+</p>
+
 <p class="table-caption" id="permissions-features">
   <strong>Table 2. </strong>Device permissions that imply device hardware use.
 </p>
@@ -1717,14 +1726,29 @@
 </tr>
 <tr>
   <td><code>ACCESS_COARSE_LOCATION</code></td>
-  <td><code>android.hardware.location.network</code> <em>and</em>
-<br><code>android.hardware.location</code></td>
+  <td>
+    <p>
+      <code>android.hardware.location</code>
+    </p>
+    <p>
+      <code>android.hardware.location.network</code>
+      (Only when target API level is 20 orlower.)
+    </p>
+  </td>
 <!--  <td></td> -->
 </tr>
 <tr>
   <td><code>ACCESS_FINE_LOCATION</code></td>
-  <td><code>android.hardware.location.gps</code> <em>and</em>
-<br><code>android.hardware.location</code></td>
+  <td>
+    <p>
+      <code>android.hardware.location</code>
+    </p>
+    <p>
+      <code>android.hardware.location.gps</code>
+      (Only when target API level is 20 orlower.)
+    </p>
+  </td>
+
 <!--  <td></td> -->
 </tr>
 
diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd
index 4995a13d..383b6c1 100644
--- a/docs/html/guide/topics/media/camera.jd
+++ b/docs/html/guide/topics/media/camera.jd
@@ -154,10 +154,16 @@
 &lt;uses-permission android:name="android.permission.RECORD_AUDIO" /&gt;
 </pre>
   </li>
-  <li><strong>Location Permission</strong> - If your application tags images with GPS location
-information, you must request location permission:
+  <li>
+    <p><strong>Location Permission</strong> - If your application tags images
+    with GPS location information, you must request the {@code ACCESS_FINE_LOCATION}
+    permission. Note that, if your app targets Android 5.0 (API level 21) or
+    higher, you also need to declare that your app uses the device's GPS:</p>
 <pre>
 &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt;
+...
+&lt;!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --&gt;
+&lt;uses-feature android:name="android.hardware.location.gps" /&gt;
 </pre>
 <p>For more information about getting user location, see
 <a href="{@docRoot}guide/topics/location/strategies.html">Location Strategies</a>.</p>
diff --git a/docs/html/guide/topics/resources/multilingual-support.jd b/docs/html/guide/topics/resources/multilingual-support.jd
index 8d8484b..28699fe 100644
--- a/docs/html/guide/topics/resources/multilingual-support.jd
+++ b/docs/html/guide/topics/resources/multilingual-support.jd
@@ -88,15 +88,17 @@
 
 <h2 id="postN">Improvements to Resource-Resolution Strategy</h2>
 <p>Android 7.0 (API level 24) brings more robust resource resolution, and
- finds better fallbacks automatically. However, to speed up resolution and
- improve
+ finds better fallbacks automatically.
+ However, to speed up resolution and improve
  maintainability, you should store resources in the most common parent dialect.
- For example, if you were storing Spanish resources in the {@code es-US}
- directory
- before, move them into the {@code es-419} directory, which contains Latin
- American Spanish.
- Similarly, if you have resource strings in a folder named {@code en-GB}, rename
- the folder to {@code en-001} (international English), because the most common
+ For example, if you were storing Spanish resources
+ in the {@code values-es-rUS} directory
+ before, move them into the {@code values-b+es+419} directory,
+ which contains Latin American Spanish.
+ Similarly, if you have resource strings in a
+ directory named {@code values-en-rGB}, rename
+ the directory to {@code values-b+en+001} (International
+ English), because the most common
  parent for <code>en-GB</code> strings is {@code en-001}.
  The following examples explain why these practices improve performance and
 reliability of resource resolution.</p>
diff --git a/docs/html/images/develop/hero-layout-editor_2x.png b/docs/html/images/develop/hero-layout-editor_2x.png
new file mode 100644
index 0000000..56dfbf3
--- /dev/null
+++ b/docs/html/images/develop/hero-layout-editor_2x.png
Binary files differ
diff --git a/docs/html/images/training/ctl-config.png b/docs/html/images/training/ctl-config.png
index 82f63c8..3a4f738 100644
--- a/docs/html/images/training/ctl-config.png
+++ b/docs/html/images/training/ctl-config.png
Binary files differ
diff --git a/docs/html/images/training/tv/playback/onboarding-fragment-diagram.png b/docs/html/images/training/tv/playback/onboarding-fragment-diagram.png
new file mode 100644
index 0000000..5839a50
--- /dev/null
+++ b/docs/html/images/training/tv/playback/onboarding-fragment-diagram.png
Binary files differ
diff --git a/docs/html/images/training/tv/playback/onboarding-fragment.png b/docs/html/images/training/tv/playback/onboarding-fragment.png
new file mode 100644
index 0000000..5b7da55
--- /dev/null
+++ b/docs/html/images/training/tv/playback/onboarding-fragment.png
Binary files differ
diff --git a/docs/html/images/training/tv/playback/onboarding-fragment_2x.png b/docs/html/images/training/tv/playback/onboarding-fragment_2x.png
new file mode 100644
index 0000000..0034be4
--- /dev/null
+++ b/docs/html/images/training/tv/playback/onboarding-fragment_2x.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index fe5dada..32360fd 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -63,6 +63,40 @@
   </div><!-- end .wrap -->
 </div><!-- end .dac-actions -->
 
+
+<section class="dac-expand dac-hero" style="background-color:#FFF0B4;">
+  <div class="wrap" style="max-width:1000px;margin-top:0;overflow:auto">
+    <div class="col-7of16 col-push-8of16 dac-hero-figure">
+      <a href="/studio/index.html">
+        <img class="dac-hero-image" style="padding-top:24px"
+          src="/studio/images/hero_image_studio_2-2_2x.png">
+      </a>
+    </div>
+    <div class="col-7of16 col-pull-6of16">
+        <h1 class="dac-hero-title" style="color:#004d40">Android Studio 2.2!</h1>
+<p class="dac-hero-description" style="color:#004d40">The latest update is
+packed with over a dozen new features, like a rewritten layout editor with the
+new constraint layout, support for Android 7.0 Nougat, Espresso test recording,
+enhanced Jack compiler / Java 8 support, expanded C++ support with CMake and
+NDK-Build, and much more!</p>
+<p class="dac-hero-description" style="color:#004d40">Android Studio 2.2
+helps you code faster and smarter.</p>
+
+<p style="margin-top:24px">
+   <a class="dac-hero-cta" href="/studio/index.html" style="color:#004d40">
+      <span class="dac-sprite dac-auto-chevron"></span>
+      Get Android Studio 2.2
+    </a>
+  &nbsp;&nbsp;&nbsp;&nbsp;<wbr>
+   <a class="dac-hero-cta" href="/studio/releases/index.html" style="color:#004d40">
+    <span class="dac-sprite dac-auto-chevron"></span>
+    See the release notes</a>
+</p>
+    </div>
+  </div>
+</section>
+
+
 <section class="dac-section dac-light" id="build-apps"><div class="wrap">
   <h1 class="dac-section-title">Build Beautiful Apps</h1>
   <div class="dac-section-subtitle">
diff --git a/docs/html/topic/libraries/data-binding/index.jd b/docs/html/topic/libraries/data-binding/index.jd
index ddcc9f2..0faa1db 100644
--- a/docs/html/topic/libraries/data-binding/index.jd
+++ b/docs/html/topic/libraries/data-binding/index.jd
@@ -162,7 +162,9 @@
 
 <p>
   To use data binding, Android Plugin for Gradle <strong>1.5.0-alpha1</strong>
-  or higher is required.
+  or higher is required. See how to <a
+href="/studio/releases/gradle-plugin.html#updating-plugin">update the Android
+Plugin for Gradle</a>.
 </p>
 
 <h2 id="build_environment">
diff --git a/docs/html/topic/libraries/support-library/features.jd b/docs/html/topic/libraries/support-library/features.jd
index 614392e..b5f189a 100755
--- a/docs/html/topic/libraries/support-library/features.jd
+++ b/docs/html/topic/libraries/support-library/features.jd
@@ -108,7 +108,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:support-compat:24.2.0
+com.android.support:support-compat:24.2.1
 </pre>
 
 <h3 id="v4-core-utils">v4 core-utils library</h3>
@@ -124,7 +124,7 @@
 </p>
 
 <pre>
-com.android.support:support-core-utils:24.2.0
+com.android.support:support-core-utils:24.2.1
 </pre>
 
 <h3 id="v4-core-ui">v4 core-ui library</h3>
@@ -141,7 +141,7 @@
 </p>
 
 <pre>
-com.android.support:support-core-ui:24.2.0
+com.android.support:support-core-ui:24.2.1
 </pre>
 
 <h3 id="v4-media-compat">v4 media-compat library</h3>
@@ -158,7 +158,7 @@
 </p>
 
 <pre>
-com.android.support:support-media-compat:24.2.0
+com.android.support:support-media-compat:24.2.1
 </pre>
 
 <h3 id="v4-fragment">v4 fragment library</h3>
@@ -178,7 +178,7 @@
 </p>
 
 <pre>
-com.android.support:support-fragment:24.2.0
+com.android.support:support-fragment:24.2.1
 </pre>
 
 <h2 id="multidex">Multidex Support Library</h2>
@@ -245,7 +245,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:appcompat-v7:24.2.0
+com.android.support:appcompat-v7:24.2.1
 </pre>
 
 
@@ -260,7 +260,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:cardview-v7:24.2.0
+com.android.support:cardview-v7:24.2.1
 </pre>
 
 
@@ -276,7 +276,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:gridlayout-v7:24.2.0
+com.android.support:gridlayout-v7:24.2.1
 </pre>
 
 
@@ -299,7 +299,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:mediarouter-v7:24.2.0
+com.android.support:mediarouter-v7:24.2.1
 </pre>
 
 <p class="caution">The v7 mediarouter library APIs introduced in Support Library
@@ -319,7 +319,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:palette-v7:24.2.0
+com.android.support:palette-v7:24.2.1
 </pre>
 
 
@@ -335,7 +335,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:recyclerview-v7:24.2.0
+com.android.support:recyclerview-v7:24.2.1
 </pre>
 
 
@@ -358,7 +358,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:preference-v7:24.2.0
+com.android.support:preference-v7:24.2.1
 </pre>
 
 <h2 id="v8">v8 Support Library</h2>
@@ -409,7 +409,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:support-v13:24.2.0
+com.android.support:support-v13:24.2.1
 </pre>
 
 
@@ -435,7 +435,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:preference-v14:24.2.0
+com.android.support:preference-v14:24.2.1
 </pre>
 
 
@@ -458,7 +458,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:preference-leanback-v17:24.2.0
+com.android.support:preference-leanback-v17:24.2.1
 </pre>
 
 
@@ -494,7 +494,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:leanback-v17:24.2.0
+com.android.support:leanback-v17:24.2.1
 </pre>
 
 
@@ -509,7 +509,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:support-annotations:24.2.0
+com.android.support:support-annotations:24.2.1
 </pre>
 
 
@@ -527,7 +527,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:design:24.2.0
+com.android.support:design:24.2.1
 </pre>
 
 
@@ -548,7 +548,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:customtabs:24.2.0
+com.android.support:customtabs:24.2.1
 </pre>
 
 
@@ -572,7 +572,7 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:percent:24.2.0
+com.android.support:percent:24.2.1
 </pre>
 
 
@@ -595,5 +595,5 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:recommendation:24.2.0
+com.android.support:recommendation:24.2.1
 </pre>
diff --git a/docs/html/topic/libraries/support-library/revisions.jd b/docs/html/topic/libraries/support-library/revisions.jd
index 4b743d5..9a24d15 100644
--- a/docs/html/topic/libraries/support-library/revisions.jd
+++ b/docs/html/topic/libraries/support-library/revisions.jd
@@ -6,9 +6,71 @@
 <p>This page provides details about the Support Library package releases.</p>
 
 <div class="toggle-content opened">
-  <p id="rev24-2-0">
+  <p id="rev24-2-1">
     <a href="#" onclick="return toggleContent(this)"><img src=
     "{@docRoot}assets/images/styles/disclosure_up.png" class=
+    "toggle-content-img" alt="">Android Support Library, revision 24.2.1</a>
+    <em>(September 2016)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+
+    <p>Fixed issues:</p>
+
+<ul>
+  <li>{@link android.support.design.widget.FloatingActionButton} can no longer
+  be anchored to indirect children of {@link
+  android.support.design.widget.CoordinatorLayout}. (AOSP issue <a href=
+  "https://code.google.com/p/android/issues/detail?id=220250">220250</a>)
+  </li>
+
+  <li>Image inside {@link
+  android.support.design.widget.CollapsingToolbarLayout} doesn’t scale properly
+  with <code>fitsSystemWindows=true</code>. (AOSP issue <a href=
+  "https://code.google.com/p/android/issues/detail?id=220389">220389</a>)
+  </li>
+
+  <li>{@link android.support.design.widget.CoordinatorLayout} throws {@link
+  java.lang.IndexOutOfBoundsException} when {@link
+  android.support.design.widget.Snackbar} is shown and dismissed. (AOSP issue
+  <a href="https://code.google.com/p/android/issues/detail?id=220762"
+  >220762</a>)
+  </li>
+
+  <li>{@link android.support.design.widget.TextInputLayout} fails to resolve
+  error text color. (AOSP issue <a href=
+  "https://code.google.com/p/android/issues/detail?id=220305">220305</a>)
+  </li>
+
+  <li>{@link android.support.v7.util.SortedList.BatchedCallback#onMoved
+  BatchedCallback.onMoved()} calls {@link
+  android.support.v7.util.SortedList.BatchedCallback#onInserted
+  BatchedCallback.onInserted()}. (AOSP issue <a href=
+  "https://code.google.com/p/android/issues/detail?id=220309">220309</a>)
+  </li>
+
+  <li>{@link android.support.design.widget.TextInputLayout} overrides right
+  compound drawable. (AOSP issue <a href=
+  "https://code.google.com/p/android/issues/detail?id=220728">220728</a>)
+  </li>
+</ul>
+
+<p>
+  A complete list of public bug fixes is available on the <a href=
+  "https://code.google.com/p/android/issues/list?can=1&q=label%3ATarget-Support-24.2.1">
+  AOSP Issue Tracker</a>.
+</p>
+
+
+  </div>
+</div>
+
+<!-- end of collapsible section: 24.2.1 -->
+
+<div class="toggle-content closed">
+  <p id="rev24-2-0">
+    <a href="#" onclick="return toggleContent(this)"><img src=
+    "{@docRoot}assets/images/styles/disclosure_down.png" class=
     "toggle-content-img" alt="">Android Support Library, revision 24.2.0</a>
     <em>(August 2016)</em>
   </p>
@@ -2912,8 +2974,6 @@
         <ul>
           <li>Added {@link android.support.v7.widget.GridLayout} to provide support for the
             {@link android.widget.GridLayout} layout object.</li>
-          <li>Added {@link android.support.v7.widget.Space} which can be used to create blank areas
-            within a {@link android.support.v7.widget.GridLayout} layout object.</li>
         </ul>
     </dl>
   </div>
diff --git a/docs/html/topic/libraries/testing-support-library/index.jd b/docs/html/topic/libraries/testing-support-library/index.jd
index 941f5c3..3d3c091 100644
--- a/docs/html/topic/libraries/testing-support-library/index.jd
+++ b/docs/html/topic/libraries/testing-support-library/index.jd
@@ -202,7 +202,7 @@
     <li><a href="{@docRoot}reference/android/support/test/filters/SdkSuppress.html">{@code @SdkSupress}</a>:
     Suppresses the test from running on a lower Android API level than the given level. For
     example, to suppress tests on all API levels lower than 18 from running, use the annotation
-    {@code @SDKSupress(minSdkVersion=18)}.
+    {@code @SdkSuppress(minSdkVersion=18)}.
     </li>
 
     <li>{@link android.test.suitebuilder.annotation.SmallTest &#64;SmallTest},
diff --git a/docs/html/training/_book.yaml b/docs/html/training/_book.yaml
index 891574f..e9635be 100644
--- a/docs/html/training/_book.yaml
+++ b/docs/html/training/_book.yaml
@@ -695,6 +695,8 @@
         value: 再生中カードを表示する
     - title: Adding a Guided Step
       path: /training/tv/playback/guided-step.html
+    - title: Introducing First-time Users to Your App
+      path: /training/tv/playback/onboarding.html
     - title: Enabling Background Playback
       path: /training/tv/playback/options.html
     - title: Adding Picture-in-picture
@@ -898,6 +900,11 @@
         value: 順応性のある UI フローの実装
       - name: zh-cn-lang
         value: 实施自适应用户界面流程
+  - title: Build a Responsive UI with ConstraintLayout
+    path: /training/constraint-layout/index.html
+    path_attributes:
+    - name: description
+      value: How to build a layout using ConstraintLayout and the Android Studio Layout Editor.
   - title: Adding the App Bar
     path: /training/appbar/index.html
     path_attributes:
@@ -1149,6 +1156,8 @@
         value: 维护兼容性
       - name: zh-tw-lang
         value: 維持相容性
+    - title: Selecting Colors with the Palette API 
+      path: /training/material/palette-colors.html
 
 - title: Best Practices for User Input
   path: /training/best-user-input.html
diff --git a/docs/html/training/accessibility/service.jd b/docs/html/training/accessibility/service.jd
index de00db7..25be81e 100755
--- a/docs/html/training/accessibility/service.jd
+++ b/docs/html/training/accessibility/service.jd
@@ -17,7 +17,7 @@
   <li><a href="#create">Create Your Accessibility Service</a></li>
   <li><a href="#configure">Configure Your Accessibility Service</a></li>
   <li><a href="#events">Respond to AccessibilityEvents</a></li>
-  <li><a href="#query">Query the View Heirarchy for More Context</a></li>
+  <li><a href="#query">Query the View Hierarchy for More Context</a></li>
 </ol>
 
 <h2>You should also read</h2>
@@ -200,7 +200,7 @@
 }
 </pre>
 
-<h2 id="query">Query the View Heirarchy for More Context</h2>
+<h2 id="query">Query the View Hierarchy's for More Context</h2>
 <p>This step is optional, but highly useful. The Android platform provides the ability for an
 {@link android.accessibilityservice.AccessibilityService} to query the view
 hierarchy, collecting information about the UI component that generated an event, and
diff --git a/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png b/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png
new file mode 100644
index 0000000..1e4867e
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/alignment-constraint-offset_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/alignment-constraint_2x.png b/docs/html/training/constraint-layout/images/alignment-constraint_2x.png
new file mode 100644
index 0000000..afe7d4a
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/alignment-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/baseline-constraint_2x.png b/docs/html/training/constraint-layout/images/baseline-constraint_2x.png
new file mode 100644
index 0000000..dfc3522
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/baseline-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png b/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png
new file mode 100644
index 0000000..be9d54f
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/constraint-fail-fixed_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/constraint-fail_2x.png b/docs/html/training/constraint-layout/images/constraint-fail_2x.png
new file mode 100644
index 0000000..3f28ef7
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/constraint-fail_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png b/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png
new file mode 100644
index 0000000..ace31a6
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png b/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png
new file mode 100644
index 0000000..0768022
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png b/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png
new file mode 100644
index 0000000..b4ffb2c
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png b/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png
new file mode 100644
index 0000000..72a4e40
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/layout-editor_2-2_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/parent-constraint_2x.png b/docs/html/training/constraint-layout/images/parent-constraint_2x.png
new file mode 100644
index 0000000..0414f1d
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/parent-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/position-constraint_2x.png b/docs/html/training/constraint-layout/images/position-constraint_2x.png
new file mode 100644
index 0000000..9f93e72
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/position-constraint_2x.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png b/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png
new file mode 100644
index 0000000..f863e5f
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png b/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png
new file mode 100644
index 0000000..d61e9b2
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png
new file mode 100644
index 0000000..9747102
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-first.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png
new file mode 100644
index 0000000..940b8495
--- /dev/null
+++ b/docs/html/training/constraint-layout/images/thumbnail-studio-constraint-second.png
Binary files differ
diff --git a/docs/html/training/constraint-layout/index.html b/docs/html/training/constraint-layout/index.html
new file mode 100644
index 0000000..62eaf15
--- /dev/null
+++ b/docs/html/training/constraint-layout/index.html
@@ -0,0 +1,498 @@
+<html devsite>
+<head>
+  <title>Build a Responsive UI with ConstraintLayout</title>
+  <meta name="book_path" value="/training/_book.yaml" />
+  <meta name="top_category" value="develop" />
+  <meta name="subcategory" value="training" />
+</head>
+<body>
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#constraints-overview">Constraints overview</a></li>
+    <li><a href="#add-constraintlayout-to-your-project">Add ConstraintLayout to your project</a></li>
+    <li><a href="#add-a-constraint">Add a constraint</a></li>
+    <li><a href="#use-autoconnect-and-infer-constraints">Use Autoconnect and Infer Constraints</a></li>
+    <li><a href="#adjust-the-view-size">Adjust the view size</a></li>
+    <li><a href="#adjust-the-constraint-bias">Adjust the constraint bias</a></li>
+    <li><a href="#adjust-the-view-margins">Adjust the view margins</a></li>
+  </ol>
+</div>
+</div>
+
+
+<p><code>ConstraintLayout</code> allows you to create large and complex layouts with a flat view
+hierarchy (no nested view groups). It's similar to <code>RelativeLayout</code> in that all views are
+layed out according to relationships between sibling views and the parent layout, but it's more
+flexible than <code>RelativeLayout</code> and easier to use with Android Studio's Layout Editor.
+</p>
+
+<p>Everything you can do with <code>ConstraintLayout</code> is available directly from the Layout Editor's visual
+tools, because the layout API and the Layout Editor were specially built for each other. So you can
+build your layout with <code>ConstraintLayout</code> entirely by drag-and-dropping instead of editing the XML.
+</p>
+
+<img src="/training/constraint-layout/images/layout-editor_2-2_2x.png" alt=""
+  width="640"/>
+<p class="img-caption"><b>Figure 1.</b> A <code>ConstraintLayout</code> in the Layout Editor</p>
+
+
+<p>
+<code>ConstraintLayout</code> is available in an API library that's compatible with Android
+2.3 (API level 9) and higher, and the new layout editor is available in Android
+Studio 2.2 and higher.
+</p>
+
+<p>
+This page provides a guide to building a layout with <code>ConstraintLayout</code> in Android
+Studio. If you'd like more information about the Layout Editor itself, see the
+Android Studio guide to <a href="/studio/write/layout-editor.html">Build a UI with
+Layout Editor</a>.
+</p>
+
+
+<h2 id="constraints-overview">Constraints overview</h2>
+<p>
+To define a view's position in <code>ConstraintLayout</code>, you must add two
+or more <em>constraints</em> for the view. Each constraint represents a connection or
+alignment to another view, the parent layout, or an invisible guideline. Each
+constraint defines the view's position along either the
+vertical or horizontal axis; so each view must have a minimum of one constraint for each
+axis, but often more are necessary.
+</p>
+
+<p>
+When you drop a view into the Layout Editor, it stays where you leave it even if
+it has no constraints. However, this is only to make editing easier; if a view has
+no constraints when you run your layout on a device, it is drawn at
+position [0,0] (the top-left corner).</p>
+
+<p>In figure 2, the layout looks good in the
+editor, but there's no vertical constraint on <code>TextView B</code>. When this
+layout draws on a device, <code>TextView B</code> horizontally aligns with the left and
+right edges of the <code>ImageView</code>, but appears at the top of the screen because
+it has no vertical constraint.
+</p>
+
+<div class="cols">
+<div class="col-1of2">
+<img src="/training/constraint-layout/images/constraint-fail_2x.png" width="100%" alt="" />
+<p class="img-caption"><strong>Figure 2.</strong> <code>TextView B</code> is missing a
+vertical constraint</p>
+</div>
+<div class="col-1of2">
+<img src="/training/constraint-layout/images/constraint-fail-fixed_2x.png" width="100%" alt="" />
+<p class="img-caption"><strong>Figure 3.</strong> <code>TextView B</code> is now vertically
+constrained to the <code>ImageView</code></p>
+</div>
+</div>
+
+<p>
+Although a missing constraint won't cause a compilation error, the Layout Editor
+indicates missing constraints as an error in the toolbar. To view the errors and
+other warnings, click <strong>Show Warnings and Errors</strong>
+<img src="/studio/images/buttons/layout-editor-errors.png" class="inline-icon" alt="" />.
+To help you avoid missing constraints, the Layout Editor can automatically add
+constraints for you with the <a
+href="#use-autoconnect-and-infer-constraints">Autoconnect and infer
+constraints</a> features.
+</p>
+
+
+<h2 id="add-constraintlayout-to-your-project">Add ConstraintLayout to your project</h2>
+<p>
+To use <code>ConstraintLayout</code> in your project, proceed as follows:
+</p>
+
+<ol>
+<li>Ensure you have the latest Constraint Layout library:
+<ol>
+ <li>Click <strong>Tools > Android > SDK Manager</strong>.
+ <li>Click the <strong>SDK Tools</strong> tab.
+ <li>Expand <strong>Support Repository</strong> and then check
+<b>ConstraintLayout for Android</b> and <b>Solver for ConstraintLayout</b>.
+Check <b>Show Package Details</b> and take note of the version you're downloading
+(you'll need this below).</p>
+ <li>Click <strong>OK</strong>.
+<li>Add the ConstraintLayout library as a dependency in your module-level
+  <code>build.gradle</code> file:
+<pre>
+dependencies {
+    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
+}
+</pre>
+  <p>The library version you download may be higher, so be sure the value you specify
+  here matches the version from step 3.</p>
+</li>
+<li>In the toolbar or sync notification, click <strong>Sync Project with Gradle
+Files</strong>.</li>
+</ol>
+</li>
+</ol>
+
+<p>Now you're ready to build your layout with <code>ConstraintLayout</code>.</p>
+
+<h3 id="convert">Convert a layout</h3>
+
+<div class="figure" style="width:415px">
+<img src="/training/constraint-layout/images/layout-editor-convert-to-constraint_2x.png"
+  width="415" alt="" />
+<p class="img-caption">
+  <b>Figure 4.</b> The menu to convert a layout to <code>ConstraintLayout</code></p>
+</div>
+
+<p>To convert an existing layout to a constraint layout, follow these steps:</p>
+<ol>
+<li>Open your layout in Android Studio and click the <strong>Design</strong> tab
+at the bottom of the editor window.
+<li>In the <strong>Component Tree</strong> window, right-click the layout and
+click <strong>Convert <em>layout</em> to ConstraintLayout</strong>.</li>
+</ol>
+
+<h3 id="createNew">Create a new layout</h3>
+
+<p>To start a new constraint layout file, follow these steps:</p>
+<ol>
+<li>Click anywhere in the <strong>Project</strong> window and then select
+<strong>File > New > XML > Layout XML</strong>.
+<li>Enter a name for the layout file and enter
+"android.support.constraint.ConstraintLayout" for the <b>Root Tag</b>.
+<li>Click <strong>Finish</strong>.</li>
+</ol>
+
+
+<h2 id="add-a-constraint">Add a constraint</h2>
+
+<p>Start by dragging a view from the <b>Palette</b> window into the editor.
+When you add a view in a <code>ConstraintLayout</code>, it displays a bounding box with square
+resizing handles on each corner and circular constraint handles on each side.
+</p>
+
+
+<div class="figure" style="width:460px">
+<div class="video-wrapper">
+<video controls poster="/training/constraint-layout/images/thumbnail-studio-constraint-first.png"
+  onclick="this.play()" width="460">
+  <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/studio-constraint-first.mp4" type="video/mp4">
+  <img src="/training/constraint-layout/images/thumbnail-studio-constraint-first" alt="" />
+</video>
+</div>
+<p class="img-caption">
+<strong>Video 1. </strong>The left side of a view is constrained to the left side of the parent
+</p>
+</div>
+
+<p>
+Click the view to select it. Then click-and-hold one of the
+constraint handles and drag the line to an available anchor point (the edge of
+another view, the edge of the layout, or a guideline). When you release, the
+constraint is made, with <a href="#adjust-the-view-margins">a default margin</a>
+separating the two views.
+</p>
+
+<p>When creating constraints, remember the following rules:</p>
+
+<ul>
+<li>Every view must have at least two constraints: one horizontal and one
+vertical.
+<li>You can create constraints only between a constraint handle and an anchor
+point that share the same plane. So a vertical plane (the left and right sides)
+of a view can be constrained only to another vertical plane; and baselines can
+constrain only to other baselines.
+<li>Each constraint handle can be used for just one constraint, but you can
+create multiple constraints (from different views) to the same anchor
+point.</li>
+</ul>
+
+
+
+<div class="figure" style="width:460px">
+<div class="video-wrapper">
+<video controls poster="/training/constraint-layout/images/thumbnail-studio-constraint-second.png"
+  onclick="this.play()" width="460">
+  <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/studio-constraint-second.mp4" type="video/mp4">
+  <img src="/training/constraint-layout/images/thumbnail-studio-constraint-second.png" alt="" />
+</video>
+</div>
+<p class="img-caption">
+<strong>Video 2. </strong>Adding a constraint that opposes an existing one
+</p>
+</div>
+
+
+
+<p>
+To remove a constraint, select the view and then click the constraint handle.
+</p>
+
+<p>If you add opposing constraints on a view, the constraint lines become squiggly
+like a spring to indicate the opposing forces, as shown in video 2. The effect
+is most visible when the view size is set to "fixed" or "wrap content," in which
+case the view is centered between the constraints. If you instead
+want the view to stretch its size to meet the constraints, <a
+href="#adjust-the-view-size">switch the size to "any size"</a>; or if you want
+to keep the current size but move the view so that it is not centered, <a
+href="#adjust-the-constraint-bias">adjust the constraint bias</a>.
+</p>
+
+
+
+<p>
+There are many ways to constrain a view, but the following constraint types
+provide the basic building blocks.
+</p>
+
+
+
+
+<h3>Parent constraint</h3>
+<div class="cols">
+<div class="col-2of3">
+  <p>
+  Connect the side of a view to the corresponding edge of the layout.
+  <p>
+  In figure 5, the left side of a view is connected to the left edge of the
+  parent layout.
+  <p>
+</div>
+<div class="col-1of3">
+  <img src="/training/constraint-layout/images/parent-constraint_2x.png" width="100%" alt="">
+  <p class="img-caption"><strong>Figure 5. </strong>A horizontal constraint to the parent</p>
+</div>
+</div>
+
+
+<h3>Position constraint</h3>
+<div class="cols">
+<div class="col-2of3">
+<p>Define the order of appearance for two views, either vertically or horizontally.</p>
+<p>In figure 6, a <code>Button</code> is constrained below an <code>ImageView</code> with a 24dp
+margin.</p>
+</div>
+<div class="col-1of3">
+  <img src="/training/constraint-layout/images/position-constraint_2x.png" width="100%" alt="">
+  <p class="img-caption"><strong>Figure 6.</strong> A vertical position constraint</p>
+</div>
+</div>
+
+
+
+<h3>Alignment constraint</h3>
+<div class="cols">
+<div class="col-1of3">
+<p>Align the edge of a view to the same edge of another view.<p>
+<p>In figure 7, the left side of a <code>Button</code> is aligned to the left side of an
+<code>ImageView</code>.</p>
+<p>You can offset the alignment by dragging the view
+inward from the constraint. For example, figure 8 shows the same
+<code>Button</code> with a 24dp offset alignment.
+The offset is defined by the constrained view's margin.</p>
+</div>
+<div class="col-1of3">
+  <img src="/training/constraint-layout/images/alignment-constraint_2x.png" width="100%" alt="">
+  <p class="img-caption"><strong>Figure 7.</strong> A horizontal alignment constraint</p>
+</div>
+<div class="col-1of3">
+  <img src="/training/constraint-layout/images/alignment-constraint-offset_2x.png" width="100%"
+    alt="">
+  <p class="img-caption"><strong>Figure 8.</strong> An offset horizontal alignment constraint</p>
+</div>
+</div>
+
+
+<h3>Baseline alignment constraint</h3>
+<div class="cols">
+<div class="col-2of3">
+<p>Align the text baseline of a view to the text baseline of another view.</p>
+<p>In figure 9, the first line of a <code>TextView</code> is aligned with the text in a
+<code>Button</code>.</p>
+<p>To create a baseline constraint, hover your mouse over the baseline handle for
+two seconds until the handle blinks white. Then click and drag the line to
+another baseline.</p>
+</div>
+<div class="col-1of3">
+  <img src="/training/constraint-layout/images/baseline-constraint_2x.png" width="100%" alt="">
+  <p class="img-caption"><strong>Figure 9.</strong> A baseline alignment constraint</p>
+</div>
+</div>
+
+
+
+
+
+<h3 id="constrain-to-a-guideline">Constrain to a guideline</h3>
+<div class="cols">
+<div class="col-1of2">
+
+<p>
+You can add a vertical or horizontal guideline to which you can attach
+constraints. You can position the guideline within the layout based on either dp
+units or percent, relative to the layout's edge.
+</p>
+
+<p>
+To create a guideline, click <strong>Guidelines</strong>
+<img src="/studio/images/buttons/layout-editor-guidelines.png" class="inline-icon" alt="" />
+in the toolbar, and then click either <strong>Add Vertical Guideline</strong>
+or <strong>Add Horizontal Guideline</strong>.
+</p>
+
+<p>
+Click the circle at the edge of the guideline to toggle the measurements used to
+position the guideline (either percent or dp units from the layout's edge).
+</p>
+
+<p>
+Guidelines are not visible to your users.
+</p>
+</div>
+
+<div class="col-1of2">
+  <div class="video-wrapper">
+  <video controls poster="/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png"
+    onclick="this.play()" width="100%">
+    <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/add-layout-guideline_2-2.mp4" type="video/mp4">
+    <img src="/training/constraint-layout/images/thumbnail-add-layout-guideline_2-2.png" alt="" />
+  </video>
+  </div>
+  <p class="img-caption"><strong>Video 3.</strong> Adding a constraint to a guideline</p>
+</div>
+</div>
+
+
+<h2 id="use-autoconnect-and-infer-constraints">Use Autoconnect and Infer Constraints</h2>
+
+<div class="figure" style="width:460px">
+<div class="video-wrapper">
+<video controls poster=""
+  onclick="this.play()" width="460">
+  <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/constraint-autoconnect_2-2.mp4" type="video/mp4">
+</video>
+</div>
+<p class="img-caption"><b>Video 4.</b> Adding a view with Autoconnect enabled</p>
+</div>
+
+<p>
+Autoconnect is a persistent mode that automatically creates two or more
+constraints for each view you add to the layout. Autoconnect is disabled by
+default. You can enable it by clicking <strong>Turn on Autoconnect</strong>
+<img src="/studio/images/buttons/layout-editor-autoconnect-on.png" class="inline-icon" alt="" />
+in the Layout Editor toolbar.
+</p>
+
+<p>While enabled, Autoconnect creates constraints for each view as you add them; it does not create
+constraints for existing views in the layout. If you drag a view once the constraints are made, the
+constraints do not change (though the margins do), so you must delete the constraints if you want to
+significantly reposition the view.</p>
+
+<p>Alternatively, you can click  <strong>Infer Constraints</strong>
+<img src="/studio/images/buttons/layout-editor-infer.png" class="inline-icon" alt="" />
+to create constraints for all views in the layout.
+</p>
+
+<p>Infer Constraints is a one-time action that scans the entire layout to determine the most
+effective set of constraints for all views, so it may create constraints between elements that are
+far from each other. Autoconnect, however, creates constraints only for the view you are adding, and
+it creates constraints to only the nearest elements. In either case, you can always modify a
+constraint by clicking the constraint handle to delete it, and then create a new constraint.</p>
+
+
+<h2 id="adjust-the-view-size">Adjust the view size</h2>
+
+<p>
+You can use the handles on each corner of the view to resize it, but doing so
+hard codes the width and height values, which you should avoid for most views
+because hard-coded view sizes cannot adapt to different content and screen
+sizes. To select from one of the dynamic sizing modes or to define more specific
+dimensions, click a view and open the <strong>Properties</strong>
+<img src="/studio/images/buttons/window-properties.png" class="inline-icon" alt="" />
+window on the right side of the editor. At the top of the window is the view
+inspector, as shown in figure 10.
+</p>
+<div class="figure" style="width:287px" >
+<img src="/training/constraint-layout/images/layout-editor-properties-callouts_2-2_2x.png" alt=""
+  width="287" />
+<p class="img-caption"><strong>Figure 10.</strong> The <b>Properties</b> window includes controls for
+<strong>(1)</strong> view size, <strong>(2)</strong> margins, and
+<strong>(3)</strong> constraint bias.</p>
+</div>
+
+<p>
+The grey square represents the selected view. The symbols inside the square
+represent the height and width settings as follows:
+</p>
+
+<ul>
+<li>
+<img src="/studio/images/buttons/layout-width-wrap.png" class="inline-icon" alt="" />
+ <strong>Wrap Content</strong>: The view expands exactly as needed to fit its
+contents.
+<li>
+<img src="/studio/images/buttons/layout-width-match.png" class="inline-icon" alt="" />
+ <strong>Any Size</strong>: The view expands exactly as needed to match the
+constraints. The actual value is 0dp because the view has no desired dimensions, but
+it resizes as needed to meet the constraints. However, if the given dimension
+has only one constraint, then the view expands to fit its contents. Another way
+to think of it is "match constraints" (instead of <code>match_parent</code>) because it
+expands the view as much as possible after accounting for the limits of each
+constraint and its margins.
+<li>
+<img src="/studio/images/buttons/layout-width-fixed.png" class="inline-icon" alt="" />
+ <strong>Fixed</strong>: You specify the dimension in the text box below or by
+resizing the view in the editor.</li>
+</ul>
+
+<p>To toggle between these settings, click the symbols.</p>
+
+<p class="note"><strong>Note</strong>: You should not use <code>match_parent</code> for any view
+in a <code>ConstraintLayout</code>. Instead use "Any Size" (<code>0dp</code>).
+</p>
+
+
+<h2 id="adjust-the-constraint-bias">Adjust the constraint bias</h2>
+
+<p>When you add a constraint to both sides of a view (and the view size for the same dimension is
+either "fixed" or "wrap content"), the view becomes centered between the two anchor points by
+default. When a view is centered, the bias is 50%. You can adjust the bias by dragging the bias
+slider in the <b>Properties</b> window or by dragging the view, as shown in video 5.</p>
+
+<div class="video-wrapper" style="max-width:740px">
+<video controls poster="/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png"
+  onclick="this.play();$(this.parentElement).addClass('playing');">
+  <source src="https://storage.googleapis.com/androiddevelopers/videos/studio/adjust-constraint-bias.mp4" type="video/mp4">
+  <img src="/training/constraint-layout/images/thumbnail-adjust-constraint-bias.png" alt="" />
+</video>
+</div>
+<p class="img-caption"><b>Video 5.</b> Adjusting the constraint bias</p>
+
+<p>If you instead want the view to stretch its size to meet the constraints, <a href="#adjust-the-
+view-size">switch the size to "any size"</a>.</p>
+
+
+<h2 id="adjust-the-view-margins">Adjust the view margins</h2>
+
+<p> To ensure that all your views are evenly spaced, click <strong>Margin</strong> <img
+src="/studio/images/buttons/layout-editor-margin.png" class="inline-icon" alt="" /> in the toolbar
+to select the default margin for each view that you add to the layout. The button changes to show
+your current margin selection. Any change you make to the default margin applies only to the views
+you add from then on. </p>
+
+
+<img src="/training/constraint-layout/images/layout-editor-margin-callout_2-2_2x.png"
+ alt="" width="232"/>
+<p class="img-caption"><strong>Figure 11.</strong> The toolbar's <b>Margin</b> button.
+Click to adjust the default margin.
+</p>
+
+<p> You can control the margin for each view in the <strong>Properties</strong> window by clicking
+the number on the line that represents each constraint (in figure 10, the margins are each set to
+16dp). </p>
+
+<p> All margins offered by the tool are factors of 8dp to help your views align to Material Design's
+<a href="https://material.google.com/layout/metrics-keylines.html">8dp square grid
+recommendations</a>. </p>
+
+</body>
+</html>
diff --git a/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png b/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png
new file mode 100644
index 0000000..d14ec32
--- /dev/null
+++ b/docs/html/training/material/images/palette-library-color-profiles_2-1_2x.png
Binary files differ
diff --git a/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png b/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png
new file mode 100644
index 0000000..883adba
--- /dev/null
+++ b/docs/html/training/material/images/palette-library-title-text-color_2-1_2x.png
Binary files differ
diff --git a/docs/html/training/material/index.jd b/docs/html/training/material/index.jd
index 4001e6b..8baa065 100644
--- a/docs/html/training/material/index.jd
+++ b/docs/html/training/material/index.jd
@@ -3,7 +3,6 @@
 page.image=images/cards/material_2x.png
 page.metaDescription=Learn how to apply material design to your apps.
 
-
 @jd:body
 
 <div id="tb-wrapper">
@@ -58,6 +57,9 @@
 
   <dt><a href="{@docRoot}training/material/compatibility.html">Maintaining Compatibility</a></dt>
   <dd>Learn how to maintain compatibility with platform versions earlier than Android 5.0.</dd>
+
+  <dt><a href="{@docRoot}training/material/palette-colors.html">Selecting Colors with the Palette API</a></dt>
+  <dd>Learn how to select colors for your app using the v7 Palette library.</dd>
 </dl>
 
 <h2>Video Training</h2>
diff --git a/docs/html/training/material/palette-colors.html b/docs/html/training/material/palette-colors.html
new file mode 100644
index 0000000..27485d2
--- /dev/null
+++ b/docs/html/training/material/palette-colors.html
@@ -0,0 +1,310 @@
+<html devsite>
+<head>
+  <title>Selecting Colors with the Palette API</title>
+  <meta name="book_path" value="/training/_book.yaml" />
+  <meta name="top_category" value="develop" />
+  <meta name="subcategory" value="training" />
+</head>
+<body>
+
+<div id="tb-wrapper">
+  <div id="tb">
+    <h2>This lesson teaches you to</h2>
+    <ol>
+      <li><a href="#set-up-the-library">Set up the library</a></li>
+      <li><a href="#create-a-palette">Create a palette</a>
+        <ol>
+          <li><a href="#generate-a-palette-instance">Generate a Palette instance</a></li>
+          <li><a href="#customize-your-palette">Customize your palette</a></li>
+        </ol>
+      </li>
+      <li><a href="#extract-color-profiles">Extract color profiles</a>
+        <ol>
+          <li><a href="#use-swatches">Use swatches to create color schemes</a></li>
+        </ol>
+      </li>
+    </ol>
+    <h2>You should also read</h2>
+    <ul>
+      <li><a href="http://www.google.com/design/spec">Material design specification</a></li>
+      <li><a href="/design/material/index.html">Material design on Android</a></li>
+    </ul>
+  </div>
+</div>
+
+<p>Good visual design is essential for a successful app, and color schemes are a primary component of design. The palette library is a
+<a href="/topic/libraries/support-library/features.html#v7-palette">support library</a>
+that extracts prominent colors from images to help you create visually engaging apps.</p>
+
+<p>You can use the palette library to design layout
+<a href="/guide/topics/ui/themes.html">themes</a> and apply custom colors to visual elements in your app.
+For example, you can use a palette to create a color-coordinated title 
+card for a song based on its album cover or to adjust an app’s toolbar color when its 
+background image changes. The <code><a 
+href="/reference/android/support/v7/graphics/Palette.html">Palette</a></code> object gives 
+you access to the colors in a <code><a
+href="/reference/android/graphics/Bitmap.html">Bitmap</a></code> 
+image while also providing six main color profiles from the bitmap to help
+inform your <a href="http://material.google.com">design choices</a>.</p>
+
+<h2 id="set-up-the-library">Set up the library</h2>
+
+<p>To use the palette library, install or update the <a
+href="/topic/libraries/support-library/index.html">Android
+Support Library</a> to version 24.0.0 or higher and follow the instructions for <a
+href="/topic/libraries/support-library/setup.html#add-library">Adding
+Support Libraries</a> to add the palette library to your app development project.</p>
+
+<p>Make sure that the version specified in your dependency identifier matches your
+app’s <code>compileSdkVersion</code>, set in the <code>build.gradle</code>
+file:</p>
+
+<pre class="prettyprint">
+android {
+  compileSdkVersion 24
+  ...
+}
+
+dependencies {
+  ...
+  compile 'com.android.support:palette-v7:24.2.1'
+}
+</pre>
+
+<p>For more information about adding the palette dependency, read about the palette
+feature in the <a
+href="/topic/libraries/support-library/features.html#v7-palette">support
+library documentation</a>.</p>
+
+<h2 id="create-a-palette">Create a palette</h2>
+
+<p>A <code>Palette</code> object gives you access to the primary colors in an
+image, as well as the corresponding colors for overlaid text. Use palettes to design
+your app’s style and to dynamically change your app’s color scheme based on a
+given source image.</p>
+
+<p>To create a palette, first instantiate a <code><a
+href="https://developer.android.com/reference/android/support/v7/graphics/Palette.Builder.html">Palette.Builder</a></code>
+from a <code>Bitmap</code>. You can then use the
+<code>Palette.Builder</code> to customize the palette before generating it. This
+section will describe palette generation and customization from a bitmap
+image.</p>
+
+<h3 id="generate-a-palette-instance">Generate a Palette instance</h3>
+
+<p>Generate a <code>Palette</code> instance using <code>Palette</code>’s
+<code><a
+href="/reference/android/support/v7/graphics/Palette.html#from(android.graphics.Bitmap)">from(Bitmap
+bitmap)</a></code> method to first create a <code>Palette.Builder</code>
+from a <code>Bitmap</code>. The builder can then generate the palette either
+synchronously or asynchronously.</p>
+
+<p>Use synchronous palette generation if you want to create the palette on
+the same thread as the method being called. If you generate the palette
+asynchronously on a different thread, use the <code><a
+href="/reference/android/support/v7/graphics/Palette.PaletteAsyncListener.html#onGenerated(android.support.v7.graphics.Palette)">onGenerated()</a></code>
+method to access the palette immediately after it has been created.</p>
+
+<p>The following code snippet provides example methods for both types of palette generation:</p>
+
+<pre class="prettyprint">
+// Generate palette synchronously and return it
+public Palette createPaletteSync(Bitmap bitmap) {
+  Palette p = Palette.from(bitmap).generate();
+  return p;
+}
+
+// Generate palette asynchronously and use it on a different
+// thread using onGenerated()
+public void createPaletteAsync(Bitmap bitmap) {
+  Palette.from(bitmap).generate(new PaletteAsyncListener() {
+    public void onGenerated(Palette p) {
+      // Use generated instance
+    }
+  });
+}
+</pre>
+
+<p>If you need to continuously generate palettes for a sorted list of images
+or objects, consider <a
+href="/reference/android/util/LruCache.html">caching</a>
+the <code>Palette</code> instances to prevent slow UI performance. You also
+should not create the palettes on your <a href="/training/articles/perf-anr.html">main thread</a>.</p>
+
+<h3 id="customize-your-palette">Customize your palette</h3>
+
+<p>The <code>Palette.Builder</code> allows you to customize your palette by
+choosing how many colors are in the resulting palette, what area of your
+image the builder uses to generate the palette, and what colors are allowed in the
+palette. For example, you can filter out the color black or ensure that the
+builder only uses the top half of an image to generate your palette.</p>
+
+<p>Fine-tune your palette’s size and colors with the following methods from
+the <code>Palette.Builder</code> class:</p>
+
+<dl>
+
+  <dt><code><a 
+  href="/reference/android/support/v7/graphics/Palette.Builder.html#addFilter(android.support.v7.graphics.Palette.Filter)">addFilter()</a></code></dt>
+  <dd>This method adds a filter that indicates what colors are allowed in the
+  resulting palette. Pass in your own<code> <a
+  href="/reference/android/support/v7/graphics/Palette.Filter.html">Palette.Filter</a></code>
+  and modify its <code>isAllowed()</code> method to determine which colors are
+  filtered from the palette.</dd>
+
+  <dt><code><a
+  href="/reference/android/support/v7/graphics/Palette.Builder.html#maximumColorCount(int)">maximumColorCount()</a></code></dt>
+  <dd>This method sets the maximum number of colors in your palette. The
+  default value is 16, and the optimal value depends on the source image. 
+  For landscapes, optimal values range from 8-16 while pictures with faces 
+  usually have values that fall between 24-32. The
+  <code>Palette.Builder</code> takes longer to generate palettes with more
+  colors.</dd>
+
+  <dt><code><a 
+  href="/reference/android/support/v7/graphics/Palette.Builder.html#setRegion(int,%20int,%20int,%20int)">setRegion()</a></code></dt>
+  <dd>This method indicates what area of the bitmap the builder uses when
+  creating the palette. You can only use this method when generating the palette from
+  a bitmap, and it does not affect the original image.</dd>
+
+  <dt><code><a
+  href="/reference/android/support/v7/graphics/Palette.Builder.html#addTarget(android.support.v7.graphics.Target)">addTarget()</a></code></dt>
+  <dd>This method allows you to perform your own color matching by adding a
+  <code><a
+  href="/reference/android/support/v7/graphics/Target.html">Target</a></code>
+  color profile to the builder. If the default <code>Target</code>s are not
+  sufficient, advanced developers can create their own <code>Target</code>s
+  using a <code><a
+  href="/reference/android/support/v7/graphics/Target.Builder.html">Target.Builder</a></code>.</dd>
+
+</dl>
+
+<h2 id="extract-color-profiles">Extract color profiles</h2>
+
+<p>Based on the <a
+href="https://material.google.com/style/color.html#">standards
+of material design</a>, the palette library extracts commonly used color
+profiles from an image. Each profile is defined by a <code><a
+href="/reference/android/support/v7/graphics/Target.html">Target</a></code>,
+and colors extracted from the bitmap image are scored against each profile
+based on saturation, luminance, and population (number of pixels in the bitmap
+represented by the color). For each profile, the color with the best score
+defines that color profile for the given image.</p>
+
+<p>By default, a <code>Palette</code> object contains 16 primary colors from
+a given image. When generating your palette, you can <a
+href="#customize-your-palette">customize</a> its number of colors using the
+<code>Palette.Builder</code>. Extracting more colors provides more potential
+matches for each color profile but also causes <code>Palette.Builder</code> to
+take longer when generating the palette.</p>
+
+<p>The palette library attempts to extract the following six color
+profiles:</p>
+
+<ul>
+  <li>Light Vibrant</li>
+  <li>Vibrant</li>
+  <li>Dark Vibrant</li>
+  <li>Light Muted</li>
+  <li>Muted</li>
+  <li>Dark Muted</li>
+</ul>
+
+<p>Each of <code>Palette</code>’s <code>get&lt;<em>Profile</em>&gt;Color()</code>
+methods returns the color in the palette associated with that particular profile,
+where <code>&lt;<em>Profile</em>&gt;</code> is replaced by the name of one of the six
+color profiles. For example, the method to get the Dark Vibrant color profile is <code><a
+href="/reference/android/support/v7/graphics/Palette.html#getDarkVibrantColor(int)">getDarkVibrantColor()</a></code>.
+Since not all images will contain all color profiles, you must also provide
+a default color to return.</p>
+
+<p>Figure 1 displays a photo and its corresponding color
+profiles from the <code>get&lt;<em>Profile</em>&gt;Color()</code> methods.</p>
+
+<img src="/training/material/images/palette-library-color-profiles_2-1_2x.png" alt="" width="624"/>
+
+<p class="img-caption"><strong>Figure 1.</strong> An example image and its
+extracted color profiles given the default maximum color count (16) for the palette.</p>
+
+<h3 id="use-swatches">Use swatches to create color schemes</h3>
+
+<p>The <code>Palette</code> class also generates <code><a 
+href="/reference/android/support/v7/graphics/Palette.Swatch.html">Palette.Swatch</a></code>
+objects for each color profile. <code>Palette.Swatch</code>
+objects contain the associated color for that profile, as well as the
+color’s population in pixels.</p>
+
+<p>Swatches have additional methods for accessing more information about the color
+profile, such as HSL values and pixel population. You can use swatches to help
+create more comprehensive color schemes and app themes using the <code><a
+href="/reference/android/support/v7/graphics/Palette.Swatch.html#getBodyTextColor()">getBodyTextColor()</a></code>
+and <code><a
+href="/reference/android/support/v7/graphics/Palette.Swatch.html#getTitleTextColor()">getTitleTextColor()</a></code>
+methods. These methods return colors appropriate for use over the swatch’s
+color.</p>
+
+<p>Each of <code>Palette</code>’s <code>get&lt;<em>Profile</em>&gt;Swatch()</code>
+methods returns the swatch associated with that particular profile,
+where <code>&lt;<em>Profile</em>&gt;</code> is replaced by the name of one of the six
+color profiles. Although the palette’s <code>get&lt;<em>Profile</em>&gt;Swatch()</code> methods
+do not require default value parameters, they return <code>null</code> if that
+particular profile does not exist in the image. Therefore, you should check that
+a swatch is not null before using it. For example, the following method 
+returns the Vibrant swatch from a palette if the swatch is not null:</p>
+
+<pre class="prettyprint">
+// Return a palette's vibrant swatch after checking that it exists
+private Palette.Swatch checkVibrantSwatch(Palette p) {
+  Palette.Swatch vibrant = p.getVibrantSwatch();
+  if (vibrant != null) {
+    return vibrant;
+  }
+  // Throw error
+}
+</pre>
+
+<p>To access all colors in a palette, the <code><a
+href="/reference/android/support/v7/graphics/Palette.html#getSwatches()">getSwatches()</a></code>
+method returns a list of all swatches generated from an
+image, including the standard six color profiles.</p>
+
+<p>The following snippet of code uses the methods from the above code snippets to
+synchronously generate a palette, get its vibrant swatch, and change the colors of a
+toolbar to match the bitmap image. Figure 2 displays the resulting image and toolbar.</p>
+
+<div class="cols">
+
+  <div class="col-2of3">
+
+<pre class="prettyprint">
+// Set the background and text colors of a toolbar given a
+// bitmap image to match
+public void setToolbarColor(Bitmap bitmap) {
+  // Generate the palette and get the vibrant swatch
+  // See the createPaletteSync() and checkVibrantSwatch() methods
+  // from the code snippets above
+  Palette p = createPaletteSync(bitmap);
+  Palette.Swatch vibrantSwatch = checkVibrantSwatch(p);
+  
+  // Set the toolbar background and text colors
+  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+  toolbar.setBackgroundColor(vibrantSwatch.getRgb());
+  toolbar.setTitleTextColor(vibrantSwatch.getTitleTextColor());
+}
+</pre>
+
+  </div>
+
+  <div class="col-1of3">
+
+    <img src="/training/material/images/palette-library-title-text-color_2-1_2x.png" alt="" width="400"/>
+
+    <p class="img-caption"><strong>Figure 2.</strong> An example image with its
+    vibrant-colored toolbar and corresponding title text color.</p>
+
+  </div>
+
+</div>
+
+</body>
+</html>
diff --git a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd
index 8fc4dca..df8b1bc 100644
--- a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd
+++ b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd
@@ -292,23 +292,21 @@
 of any app failures.</p>
 
 <p>
-  Before you can start using Firebase Test Lab, you need to:
+  Before you can start using Firebase Test Lab, you need to do the following
+unless you already have a Google account and a Firebase project with the Blaze
+billing plan enabled:
 </p>
 
 <ol>
-  <li>
-    <a href="https://console.developers.google.com/freetrial">Create a
-    Google Cloud Platform account</a> to use with active billing.
-  </li>
-
-  <li>
-    <a href="https://support.google.com/cloud/answer/6251787">Create a Google
-    Cloud project</a> for your app.
-  </li>
-
-  <li>
-    <a href="https://support.google.com/cloud/answer/6288653">Set up an active
-    billing account</a> and associate it with the project you just created.
+  <li><a href="https://accounts.google.com/">Create a Google account</a>,
+  if you don't have one already.</li>
+  <li>In the <a href="https://console.firebase.google.com/">Firebase
+  console</a>, click <b>Create New Project</b>.</li>
+  <li>In the Firebase console, click <b>Upgrade</b>, and then click <b>Select
+Plan</b> in the <b>Blaze</b> plan column.
+    <p class="note"><b>Note</b>: To learn about billing,
+see <a href="https://firebase.google.com/docs/test-lab/overview#billing">Test
+Lab billing</a>.</p>
   </li>
 </ol>
 
@@ -318,10 +316,10 @@
 </h4>
 
 <p>
-  Android Studio provides integrated tools that allow you to configure how you
- want to deploy your tests to Firebase Test Lab. After you have created a Google
-  Cloud project with active billing, you can create a test configuration and
-  run your tests:
+Android Studio provides integrated tools that allow you to configure how you
+want to deploy your tests to Firebase Test Lab. After you have created a
+Firebase project with Blaze plan billing, you can create a test configuration
+and run your tests:
 </p>
 
 <ol>
@@ -329,7 +327,8 @@
   the main menu.
   </li>
 
-  <li>Click <strong>Add New Configuration (+)</strong> and select
+  <li>Click <strong>Add New Configuration</strong> <img
+src="/studio/images/buttons/ic_plus.png" alt="" class="inline-icon"/> and select
   <strong>Android Tests</strong>.
   </li>
 
@@ -340,7 +339,7 @@
       </li>
 
       <li>From the <em>Target</em> drop-down menu under <em>Deployment Target
-      Options</em>, select <strong>Cloud Test Lab Device Matrix</strong>.
+      Options</em>, select <strong>Firebase Test Lab Device Matrix</strong>.
       </li>
 
       <li>If you are not logged in, click <strong>Connect to Google Cloud
@@ -348,9 +347,9 @@
       </li>
 
       <li>Next to <em>Cloud Project</em>, click the <img src=
-      "{@docRoot}images/tools/as-wrench.png" alt="wrench and nut" style=
-      "vertical-align:bottom;margin:0;"> button and select your Google Cloud
-      Platform project from the list.
+      "{@docRoot}images/tools/as-wrench.png" alt="" class="inline-icon"/>
+      button and select your Firebase
+      project from the list.
       </li>
     </ol>
   </li>
@@ -359,7 +358,7 @@
     <ol type="a">
       <li>Next to the <em>Matrix Configuration</em> drop-down list, click <strong>
         Open Dialog</strong> <img src="{@docRoot}images/tools/as-launchavdm.png"
-        alt="ellipses button" style="vertical-align:bottom;margin:0;">.
+        alt="" class="inline-icon">.
       </li>
 
       <li>Click <strong>Add New Configuration (+)</strong>.
@@ -385,8 +384,7 @@
   </li>
 
   <li>Run your tests by clicking <strong>Run</strong> <img src=
-  "{@docRoot}images/tools/as-run.png" alt="" style=
-  "vertical-align:bottom;margin:0;">.
+  "{@docRoot}images/tools/as-run.png" alt="" class="inline-icon"/>.
   </li>
 </ol>
 
@@ -404,7 +402,7 @@
   When Firebase Test Lab completes running your tests, the <em>Run</em> window
   will open to show the results, as shown in figure 2. You may need to click
   <strong>Show Passed</strong> <img src="{@docRoot}images/tools/as-ok.png" alt=
-  "" style="vertical-align:bottom;margin:0;"> to see all your executed tests.
+  "" class="inline-icon"/> to see all your executed tests.
 </p>
 
 <img src="{@docRoot}images/training/ctl-test-results.png" alt="">
@@ -416,15 +414,7 @@
 
 <p>
   You can also analyze your tests on the web by following the link displayed at
-  the beginning of the test execution log in the <em>Run</em> window, as shown
-  in figure 3.
-</p>
-
-<img src="{@docRoot}images/training/ctl-exec-log.png" alt="">
-
-<p class="img-caption">
-  <strong>Figure 3.</strong> Click the link to view detailed test results on
-  the web.
+  the beginning of the test execution log in the <em>Run</em> window.
 </p>
 
 <p>
diff --git a/docs/html/training/tv/playback/index.jd b/docs/html/training/tv/playback/index.jd
index d5e4e67..34c6287 100644
--- a/docs/html/training/tv/playback/index.jd
+++ b/docs/html/training/tv/playback/index.jd
@@ -69,6 +69,10 @@
     <dd>Learn how to use the Leanback support library to guide a user through a series of
     decisions.</dd>
 
+  <dt><b><a href="onboarding.html">Introducing First-time Users to Your App</a></b></dt>
+    <dd>Learn how to use the Leanback support library to show first-time users
+    how to get the most out of your app.</dd>
+
   <dt><b><a href="options.html">Enabling Background Playback</a></b></dt>
     <dd>Learn how to continue playback when the user clicks on <strong>Home</strong>.</dd>
 </dl>
diff --git a/docs/html/training/tv/playback/onboarding.jd b/docs/html/training/tv/playback/onboarding.jd
new file mode 100644
index 0000000..bb41bec
--- /dev/null
+++ b/docs/html/training/tv/playback/onboarding.jd
@@ -0,0 +1,377 @@
+page.title=Introducing First-time Users to Your App
+page.tags=tv,onboarding,OnboardingFragment
+page.keywords=tv,onboarding,OnboardingFragment
+helpoutsWidget=true
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>This lesson teaches you to</h2>
+  <ol>
+    <li><a href="#addFragment">Add an OnboardingFragment</a></li>
+    <li><a href="#pageContent">Add OnboardingFragment Pages</a></li>
+    <li><a href="#logoScreen">Add an Initial Logo Screen</a></li>
+    <li><a href="#pageAnimations">Customize Page Animations</a></li>
+    <li><a href="#themes">Customize Themes</a></li>
+  </ol>
+  <h2>Try it out</h2>
+  <ul>
+    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
+    Leanback sample app</a></li>
+  </ul>
+</div>
+</div>
+
+<p>
+To show a first-time user how to get the most from your app, present
+onboarding information at app startup. Here are some examples of onboarding
+information:
+</p>
+
+<ul>
+<li>Present detailed information on which channels are available when a user
+first accesses a channel app.</li>
+<li>Call attention to noteworthy features in your app.</li>
+<li>Illustrate any required or recommended steps that users should take when
+using the app for the first time.</li>
+</ul>
+
+<p>The <a href=
+"{@docRoot}tools/support-library/features.html#v17-leanback">v17 Leanback
+support library</a> provides the
+{@link android.support.v17.leanback.app.OnboardingFragment} class for
+presenting first-time user information. This lesson describes how to use the
+{@link android.support.v17.leanback.app.OnboardingFragment} class to present
+introductory information that is shown when the app launches for the first
+time. {@link android.support.v17.leanback.app.OnboardingFragment} uses TV UI
+best practices to present the information in a way that matches TV UI styles,
+and is easy to navigate on TV devices.</p>
+
+<img src="{@docRoot}images/training/tv/playback/onboarding-fragment.png"
+srcset="{@docRoot}images/training/tv/playback/onboarding-fragment.png 1x,
+{@docRoot}images/training/tv/playback/onboarding-fragment_2x.png 2x" />
+<p class="img-caption"><strong>Figure 1.</strong> An example
+OnboardingFragment.</p>
+
+<p>Your {@link android.support.v17.leanback.app.OnboardingFragment} should
+not contain UI elements that require user input, such as buttons and fields.
+Similarly, it should not be used as a UI element for a task the user will do
+regularly. If you need to present a multi-page UI that requires
+user input, consider using a
+{@link android.support.v17.leanback.app.GuidedStepFragment}.</p>
+
+<h2 id="addFragment">Add an OnboardingFragment</h2>
+
+<p>To add an {@link android.support.v17.leanback.app.OnboardingFragment}
+to your app, implement a class that extends
+the {@link android.support.v17.leanback.app.OnboardingFragment} class. Add
+this fragment to an activity, either via the activity's layout XML, or
+programmatically. Make sure the activity or
+fragment is using a theme derived from
+{@link android.support.v17.leanback.R.style#Theme_Leanback_Onboarding},
+as described in <a href="#themes">Customize Themes</a>.</p>
+
+<p>In the {@link android.app.Activity#onCreate onCreate()} method of your
+app's main activity, call
+{@link android.app.Activity#startActivity startActivity()}
+with an {@link android.content.Intent} that points to your
+{@link android.support.v17.leanback.app.OnboardingFragment OnboardingFragment's}
+parent activity. This ensures that your
+{@link android.support.v17.leanback.app.OnboardingFragment} appears as
+soon as your app starts.<p>
+
+<p>To ensure that the
+{@link android.support.v17.leanback.app.OnboardingFragment} only appears the
+first time that the user starts your app, use a
+{@link android.content.SharedPreferences} object
+to track whether the user has already viewed the
+{@link android.support.v17.leanback.app.OnboardingFragment}. Define a boolean
+value that changes to true when the user finishes viewing the
+{@link android.support.v17.leanback.app.OnboardingFragment}. Check
+this value in your main activity’s
+{@link android.app.Activity#onCreate onCreate()}, and only start the
+{@link android.support.v17.leanback.app.OnboardingFragment} parent activity if
+the value is false. The following example shows an override of
+{@link android.app.Activity#onCreate onCreate()} that checks for a
+{@link android.content.SharedPreferences} value and, if not set to true, calls
+{@link android.app.Activity#startActivity startActivity()} to
+show the {@link android.support.v17.leanback.app.OnboardingFragment}:</p>
+
+<pre>
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_main);
+    SharedPreferences sharedPreferences =
+            PreferenceManager.getDefaultSharedPreferences(this);
+    // Check if we need to display our OnboardingFragment
+    if (!sharedPreferences.getBoolean(
+            MyOnboardingFragment.COMPLETED_ONBOARDING_PREF_NAME, false)) {
+        // The user hasn't seen the OnboardingFragment yet, so show it
+        startActivity(new Intent(this, OnboardingActivity.class));
+    }
+}
+</pre>
+
+<p>After the user views the
+{@link android.support.v17.leanback.app.OnboardingFragment}, mark it as viewed
+using the {@link android.content.SharedPreferences} object. To do this, in your
+{@link android.support.v17.leanback.app.OnboardingFragment}, override
+{@link android.support.v17.leanback.app.OnboardingFragment#onFinishFragment
+onFinishFragment()} and set your {@link android.content.SharedPreferences} value
+to true, as shown in the following example:
+
+<pre>
+&#64;Override
+protected void onFinishFragment() {
+    super.onFinishFragment();
+    // User has seen OnboardingFragment, so mark our SharedPreferences
+    // flag as completed so that we don't show our OnboardingFragment
+    // the next time the user launches the app.
+    SharedPreferences.Editor sharedPreferencesEditor =
+            PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
+    sharedPreferencesEditor.putBoolean(
+            COMPLETED_ONBOARDING_PREF_NAME, true);
+    sharedPreferencesEditor.apply();
+}
+</pre>
+
+<h2 id="pageContent">Add OnboardingFragment Pages</h2>
+
+<p>After you add your
+{@link android.support.v17.leanback.app.OnboardingFragment}, you need to define
+the onboarding pages. An
+{@link android.support.v17.leanback.app.OnboardingFragment} displays content
+in a series of ordered pages. Each page can have a title, description, and
+several sub-views that can contain images or animations.</p>
+
+<img src="{@docRoot}images/training/tv/playback/onboarding-fragment-diagram.png"
+/><p class="img-caption"><strong>Figure 2.</strong> OnboardingFragment
+page elements.
+</p>
+
+<p>Figure 2 shows an example page with callouts marking customizable page
+elements that your {@link android.support.v17.leanback.app.OnboardingFragment}
+can provide. The page elements are:</p>
+
+<ol>
+<li>The page title.</li>
+<li>The page description.</li>
+<li>The page content view, in this case a simple green checkmark in a grey box.
+This view is optional. Use this view to illustrate page details such as a
+screenshot that highlights the app feature that the page describes.</li>
+<li>The page background view, in this case a simple blue gradient. This view
+always renders behind other views on the page. This view is optional.</li>
+<li>The page foreground view, in this case a logo. This view always renders
+in front of all other views on the page. This view is optional.</li>
+</ol>
+
+<p>Initialize page information when your
+{@link android.support.v17.leanback.app.OnboardingFragment} is first created
+or attached to the parent activity, as the system requests page
+information when it creates the fragment's view. You can initialize page
+information in your class constructor or in an override of
+{@link android.app.Fragment#onAttach onAttach()}.</p>
+
+<p>Override each of the following methods that provide page information
+to the system:</p>
+
+<ul>
+<li>{@link android.support.v17.leanback.app.OnboardingFragment#getPageCount
+getPageCount()} returns the number of pages in your
+{@link android.support.v17.leanback.app.OnboardingFragment}.</li>
+<li>{@link android.support.v17.leanback.app.OnboardingFragment#getPageTitle
+getPageTitle()} returns the title for the requested page number.</li>
+<li>{@link android.support.v17.leanback.app.OnboardingFragment#getPageDescription
+getPagedescription()} returns the description for the requested page
+number.</li>
+</ul>
+
+<p>Override each of the following methods to provide optional sub-views used
+to display images or animations:</p>
+
+<ul>
+<li>{@link android.support.v17.leanback.app.OnboardingFragment#onCreateBackgroundView
+onCreateBackgroundView()} returns a {@link android.view.View} that you
+create to act as the background view, or null if no background view is needed.
+<li>{@link android.support.v17.leanback.app.OnboardingFragment#onCreateContentView
+onCreateContentView()} returns a {@link android.view.View} that you
+create to act as the content view, or null if no content view is needed.
+<li>{@link android.support.v17.leanback.app.OnboardingFragment#onCreateForegroundView
+onCreateForegroundView()} returns a {@link android.view.View} that you
+create to act as the foreground view, or null if no foreground view is needed.
+</ul>
+
+<p>The system adds the {@link android.view.View} that you create to the page
+layout. The following example overrides
+{@link android.support.v17.leanback.app.OnboardingFragment#onCreateContentView
+onCreateContentView()} and returns an {@link android.widget.ImageView}:</p>
+
+<pre>
+private ImageView mContentView;
+...
+&#64;Override
+protected View onCreateContentView(LayoutInflater inflater, ViewGroup container) {
+    mContentView = new ImageView(getContext());
+    mContentView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+    mContentView.setImageResource(R.drawable.onboarding_content_view);
+    mContentView.setPadding(0, 32, 0, 32);
+    return mContentView;
+}
+</pre>
+
+<h2 id="logoScreen">Add an Initial Logo Screen</h2>
+
+<p>Your {@link android.support.v17.leanback.app.OnboardingFragment} can start
+with an optional logo screen that introduces your app. If you want to display
+a {@link android.graphics.drawable.Drawable} as your logo screen, in your
+{@link android.support.v17.leanback.app.OnboardingFragment OnboardingFragment's}
+{@link android.app.Fragment#onCreate onCreate()} method, call
+{@link android.support.v17.leanback.app.OnboardingFragment#setLogoResourceId
+setLogoResourceId()} with the ID of your
+{@link android.graphics.drawable.Drawable}. The
+system will fade in and briefly display this
+{@link android.graphics.drawable.Drawable}, and then fade out the
+{@link android.graphics.drawable.Drawable}
+before displaying the first page of your
+{@link android.support.v17.leanback.app.OnboardingFragment}.</p>
+
+<p>If you want to provide a custom animation for your logo screen, instead of
+calling
+{@link android.support.v17.leanback.app.OnboardingFragment#setLogoResourceId
+setLogoResourceId()}, override
+{@link android.support.v17.leanback.app.OnboardingFragment#onCreateLogoAnimation
+onCreateLogoAnimation()} and return an {@link android.animation.Animator}
+object that renders your custom animation, as shown in the following example:
+</p>
+
+<pre>
+&#64;Override
+public Animator onCreateLogoAnimation() {
+    return AnimatorInflater.loadAnimator(mContext,
+            R.animator.onboarding_logo_screen_animation);
+}
+</pre>
+
+<h2 id="pageAnimations">Customize Page Animations</h2>
+
+<p>The system uses default animations when displaying the first page of your
+{@link android.support.v17.leanback.app.OnboardingFragment} and when the user
+navigates to a different page. You can customize these animations by
+overriding methods in your
+{@link android.support.v17.leanback.app.OnboardingFragment}.</p>
+
+<p>To customize the animation that appears on your first page,
+override
+{@link android.support.v17.leanback.app.OnboardingFragment#onCreateEnterAnimation
+onCreateEnterAnimation()} and return an {@link android.animation.Animator}.
+The following example creates an
+{@link android.animation.Animator} that scales the content view
+horizontally:</p>
+
+<pre>
+&#64;Override
+protected Animator onCreateEnterAnimation() {
+    Animator startAnimator = ObjectAnimator.ofFloat(mContentView,
+            View.SCALE_X, 0.2f, 1.0f).setDuration(ANIMATION_DURATION);
+    return startAnimator;
+}
+</pre>
+
+<p>To customize the animation used when the user navigates to a different page,
+override
+{@link android.support.v17.leanback.app.OnboardingFragment#onPageChanged
+onPageChanged()}. In your
+{@link android.support.v17.leanback.app.OnboardingFragment#onPageChanged
+onPageChanged()} method, create {@link android.animation.Animator Animators}
+that remove the previous page and display the next page, add these to an
+{@link android.animation.AnimatorSet}, and play the set. The following
+example uses a fade-out animation to remove the previous page, updates the
+content view image, and uses a fade-in animation to display the next page:</p>
+
+<pre>
+&#64;Override
+protected void onPageChanged(final int newPage, int previousPage) {
+    // Create a fade-out animation used to fade out previousPage and, once
+    // done, swaps the contentView image with the next page's image.
+    Animator fadeOut = ObjectAnimator.ofFloat(mContentView,
+            View.ALPHA, 1.0f, 0.0f).setDuration(ANIMATION_DURATION);
+    fadeOut.addListener(new AnimatorListenerAdapter() {
+        &#64;Override
+        public void onAnimationEnd(Animator animation) {
+            mContentView.setImageResource(pageImages[newPage]);
+        }
+    });
+    // Create a fade-in animation used to fade in nextPage
+    Animator fadeIn = ObjectAnimator.ofFloat(mContentView,
+            View.ALPHA, 0.0f, 1.0f).setDuration(ANIMATION_DURATION);
+    // Create AnimatorSet with our fade-out and fade-in animators, and start it
+    AnimatorSet set = new AnimatorSet();
+    set.playSequentially(fadeOut, fadeIn);
+    set.start();
+}
+</pre>
+
+<p>For more details about how to create
+{@link android.animation.Animator Animators} and
+{@link android.animation.AnimatorSet AnimatorSets}, see
+<a href="https://developer.android.com/guide/topics/graphics/prop-animation.html">
+Property Animations</a>.</p>
+
+<h2 id="themes">Customize Themes</h2>
+
+<p>Any {@link android.support.v17.leanback.app.OnboardingFragment}
+implementation must use either the
+{@link android.support.v17.leanback.R.style#Theme_Leanback_Onboarding} theme
+or a theme that inherits from
+{@link android.support.v17.leanback.R.style#Theme_Leanback_Onboarding}. Set the
+theme for your {@link android.support.v17.leanback.app.OnboardingFragment} by
+doing one of the following:</p>
+
+<ul>
+<li>Set the {@link android.support.v17.leanback.app.OnboardingFragment
+OnboardingFragment's} parent activity to use the desired theme. The following
+example shows how to set an activity to use
+{@link android.support.v17.leanback.R.style#Theme_Leanback_Onboarding} in the
+app manifest:
+<pre>
+&lt;activity
+   android:name=".OnboardingActivity"
+   android:enabled="true"
+   android:exported="true"
+   android:theme="&#64;style/Theme.Leanback.Onboarding"&gt;
+&lt;/activity&gt;
+</pre>
+</li>
+<li>
+Set the theme in the parent activity by using the
+{@link android.support.v17.leanback.R.styleable#LeanbackOnboardingTheme_onboardingTheme}
+attribute in a custom activity theme. Point this attribute to another
+custom theme that only the
+{@link android.support.v17.leanback.app.OnboardingFragment}
+objects in your activity use. Use this approach if your activity already uses
+a custom theme and you don't want to apply
+{@link android.support.v17.leanback.app.OnboardingFragment} styles to other
+views in the activity.
+</li>
+<li>Override
+{@link android.support.v17.leanback.app.OnboardingFragment#onProvideTheme
+onProvideTheme()} and return the desired theme. Use this approach if
+multiple activities use your
+{@link android.support.v17.leanback.app.OnboardingFragment}
+or if the parent activity can't use the desired theme.
+The following example overrides
+{@link android.support.v17.leanback.app.OnboardingFragment#onProvideTheme
+onProvideTheme()} and returns
+{@link android.support.v17.leanback.R.style#Theme_Leanback_Onboarding}:
+<pre>
+&#64;Override
+public int onProvideTheme() {
+   return R.style.Theme_Leanback_Onboarding;
+}
+</pre>
+</li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/training/tv/start/hardware.jd b/docs/html/training/tv/start/hardware.jd
index 97cf7ff..0639871 100644
--- a/docs/html/training/tv/start/hardware.jd
+++ b/docs/html/training/tv/start/hardware.jd
@@ -227,13 +227,19 @@
   </tr>
   <tr>
     <td>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</td>
-    <td>{@code android.hardware.location} <em>and</em> <br>
-      {@code android.hardware.location.network}</td>
+    <td>
+      <p>{@code android.hardware.location}</p>
+      <p>{@code android.hardware.location.network} (Target API level 20 or lower
+      only.)</p>
+    </td>
   </tr>
   <tr>
     <td>{@link android.Manifest.permission#ACCESS_FINE_LOCATION}</td>
-    <td>{@code android.hardware.location} <em>and</em> <br>
-      {@code android.hardware.location.gps}</td>
+    <td>
+      <p>{@code android.hardware.location}</p>
+      <p>{@code android.hardware.location.gps} (Target API level 20 or lower
+      only.)</p>
+    </td>
   </tr>
 </table>
 
@@ -246,6 +252,13 @@
   required ({@code android:required="false"}).
 </p>
 
+<p class="note">
+  <strong>Note:</strong> If your app targets Android 5.0 (API level 21) or
+  higher and uses the <code>ACCESS_COARSE_LOCATION</code> or
+  <code>ACCESS_FINE_LOCATION</code> permission, users can still install your
+  app on a TV device, even if the TV device doesn't have a network card or a GPS
+  receiver.
+</p>
 
 <h3 id="check-features">Checking for hardware features</h2>
 
diff --git a/docs/image_sources/training/tv/playback/onboarding-fragment-diagram.graffle.zip b/docs/image_sources/training/tv/playback/onboarding-fragment-diagram.graffle.zip
new file mode 100644
index 0000000..89a799b
--- /dev/null
+++ b/docs/image_sources/training/tv/playback/onboarding-fragment-diagram.graffle.zip
Binary files differ
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
index 08c7b95..d0797a9 100644
--- a/drm/jni/Android.mk
+++ b/drm/jni/Android.mk
@@ -37,7 +37,6 @@
     $(TOP)/frameworks/av/drm/libdrmframework/include \
     $(TOP)/frameworks/av/drm/libdrmframework/plugins/common/include \
     $(TOP)/frameworks/av/include \
-    $(TOP)/libcore/include
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index b2adcf6..08a68f4 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -21,23 +21,8 @@
 */
 public class ComposeShader extends Shader {
 
-    private static final int TYPE_XFERMODE = 1;
-    private static final int TYPE_PORTERDUFFMODE = 2;
-
-    /**
-     * Type of the ComposeShader: can be either TYPE_XFERMODE or TYPE_PORTERDUFFMODE
-     */
-    private int mType;
-
-    private Xfermode mXferMode;
-    private PorterDuff.Mode mPorterDuffMode;
-
-    /**
-     * Hold onto the shaders to avoid GC.
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
+    private int mPorterDuffMode;
     private final Shader mShaderA;
-    @SuppressWarnings({"UnusedDeclaration"})
     private final Shader mShaderB;
 
     /** Create a new compose shader, given shaders A, B, and a combining mode.
@@ -49,12 +34,7 @@
                         is null, then SRC_OVER is assumed.
     */
     public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
-        mType = TYPE_XFERMODE;
-        mShaderA = shaderA;
-        mShaderB = shaderB;
-        mXferMode = mode;
-        init(nativeCreate1(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
-                (mode != null) ? mode.native_instance : 0));
+        this(shaderA, shaderB, mode.porterDuffMode);
     }
 
     /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -65,12 +45,15 @@
         @param mode     The PorterDuff mode that combines the colors from the two shaders.
     */
     public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
-        mType = TYPE_PORTERDUFFMODE;
+        this(shaderA, shaderB, mode.nativeInt);
+    }
+
+    private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) {
         mShaderA = shaderA;
         mShaderB = shaderB;
-        mPorterDuffMode = mode;
-        init(nativeCreate2(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
-                mode.nativeInt));
+        mPorterDuffMode = nativeMode;
+        init(nativeCreate(shaderA.getNativeInstance(), shaderB.getNativeInstance(),
+                nativeMode));
     }
 
     /**
@@ -78,24 +61,12 @@
      */
     @Override
     protected Shader copy() {
-        final ComposeShader copy;
-        switch (mType) {
-            case TYPE_XFERMODE:
-                copy = new ComposeShader(mShaderA.copy(), mShaderB.copy(), mXferMode);
-                break;
-            case TYPE_PORTERDUFFMODE:
-                copy = new ComposeShader(mShaderA.copy(), mShaderB.copy(), mPorterDuffMode);
-                break;
-            default:
-                throw new IllegalArgumentException(
-                        "ComposeShader should be created with either Xfermode or PorterDuffMode");
-        }
+        final ComposeShader copy = new ComposeShader(
+                mShaderA.copy(), mShaderB.copy(), mPorterDuffMode);
         copyLocalMatrix(copy);
         return copy;
     }
 
-    private static native long nativeCreate1(long native_shaderA, long native_shaderB,
-            long native_mode);
-    private static native long nativeCreate2(long native_shaderA, long native_shaderB,
+    private static native long nativeCreate(long native_shaderA, long native_shaderB,
             int porterDuffMode);
 }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 81bbfa9..4abe50f 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1096,10 +1096,11 @@
      * @return         xfermode
      */
     public Xfermode setXfermode(Xfermode xfermode) {
-        long xfermodeNative = 0;
-        if (xfermode != null)
-            xfermodeNative = xfermode.native_instance;
-        nSetXfermode(mNativePaint, xfermodeNative);
+        int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
+        int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
+        if (newMode != curMode) {
+            nSetXfermode(mNativePaint, newMode);
+        }
         mXfermode = xfermode;
         return xfermode;
     }
@@ -2694,8 +2695,7 @@
     private static native long nSetShader(long paintPtr, long shader);
     private static native long nSetColorFilter(long paintPtr,
                                                     long filter);
-    private static native long nSetXfermode(long paintPtr,
-                                                  long xfermode);
+    private static native void nSetXfermode(long paintPtr, int xfermode);
     private static native long nSetPathEffect(long paintPtr,
                                                     long effect);
     private static native long nSetMaskFilter(long paintPtr,
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index d9d7689..5104410 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -18,19 +18,11 @@
 
 public class PorterDuffXfermode extends Xfermode {
     /**
-     * @hide
-     */
-    public final PorterDuff.Mode mode;
-
-    /**
      * Create an xfermode that uses the specified porter-duff mode.
      *
      * @param mode           The porter-duff mode that is applied
      */
     public PorterDuffXfermode(PorterDuff.Mode mode) {
-        this.mode = mode;
-        native_instance = nativeCreateXfermode(mode.nativeInt);
+        porterDuffMode = mode.nativeInt;
     }
-    
-    private static native long nativeCreateXfermode(int mode);
 }
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index c049e41..a5da5d0 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -29,17 +29,6 @@
  * objects drawn with that paint have the xfermode applied.
  */
 public class Xfermode {
-
-    protected void finalize() throws Throwable {
-        try {
-            finalizer(native_instance);
-            native_instance = 0;
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private static native void finalizer(long native_instance);
-
-    long native_instance;
+    static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
+    int porterDuffMode = DEFAULT;
 }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 3aca867..af20f8a 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -573,6 +573,12 @@
      * </p>
      */
     public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
+        if (getColorFilter() instanceof PorterDuffColorFilter) {
+            PorterDuffColorFilter existing = (PorterDuffColorFilter) getColorFilter();
+            if (existing.getColor() == color && existing.getMode() == mode) {
+                return;
+            }
+        }
         setColorFilter(new PorterDuffColorFilter(color, mode));
     }
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 513e376..81a1831 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,6 +2,8 @@
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
+BUGREPORT_FONT_CACHE_USAGE := true
+
 # Enables fine-grained GLES error checking
 # If set to true, every GLES call is wrapped & error checked
 # Has moderate overhead
@@ -135,6 +137,13 @@
 # clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
 hwui_cflags += -Wno-missing-braces
 
+ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE))
+    hwui_src_files += \
+        font/FontCacheHistoryTracker.cpp
+    hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE
+endif
+
+
 ifndef HWUI_COMPILE_SYMBOLS
     hwui_cflags += -fvisibility=hidden
 endif
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index fe3d859..741cdcc 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -21,6 +21,9 @@
 #include "Properties.h"
 #include "renderstate/RenderState.h"
 #include "ShadowTessellator.h"
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+#include "font/FontCacheHistoryTracker.h"
+#endif
 #include "utils/GLUtils.h"
 
 #include <cutils/properties.h>
@@ -191,12 +194,7 @@
     log.appendFormat("  PatchCache           %8d / %8d\n",
             patchCache.getSize(), patchCache.getMaxSize());
 
-    const uint32_t sizeA8 = fontRenderer.getFontRendererSize(GL_ALPHA);
-    const uint32_t sizeRGBA = fontRenderer.getFontRendererSize(GL_RGBA);
-    log.appendFormat("  FontRenderer A8    %8d / %8d\n", sizeA8, sizeA8);
-    log.appendFormat("  FontRenderer RGBA  %8d / %8d\n", sizeRGBA, sizeRGBA);
-    log.appendFormat("  FontRenderer total %8d / %8d\n", sizeA8 + sizeRGBA,
-            sizeA8 + sizeRGBA);
+    fontRenderer.dumpMemoryUsage(log);
 
     log.appendFormat("Other:\n");
     log.appendFormat("  FboCache             %8d / %8d\n",
@@ -209,11 +207,14 @@
     total += tessellationCache.getSize();
     total += dropShadowCache.getSize();
     total += patchCache.getSize();
-    total += fontRenderer.getFontRendererSize(GL_ALPHA);
-    total += fontRenderer.getFontRendererSize(GL_RGBA);
+    total += fontRenderer.getSize();
 
     log.appendFormat("Total memory usage:\n");
     log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
+
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+    fontRenderer.getFontRenderer().historyTracker().dump(log);
+#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 25dc92c..9b60dfc 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -151,10 +151,17 @@
 
     for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
         mACacheTextures[i]->init();
+
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+        mHistoryTracker.glyphsCleared(mACacheTextures[i]);
+#endif
     }
 
     for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
         mRGBACacheTextures[i]->init();
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+        mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]);
+#endif
     }
 
     mDrawn = false;
@@ -166,6 +173,9 @@
         CacheTexture* cacheTexture = cacheTextures[i];
         if (cacheTexture->getPixelBuffer()) {
             cacheTexture->init();
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+            mHistoryTracker.glyphsCleared(cacheTexture);
+#endif
             LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
             while (it.next()) {
                 it.value()->invalidateTextureCache(cacheTexture);
@@ -368,6 +378,10 @@
     }
 
     cachedGlyph->mIsValid = true;
+
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+    mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight);
+#endif
 }
 
 CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
@@ -730,18 +744,67 @@
     return size;
 }
 
-uint32_t FontRenderer::getCacheSize(GLenum format) const {
-    switch (format) {
-        case GL_ALPHA: {
-            return calculateCacheSize(mACacheTextures);
-        }
-        case GL_RGBA: {
-            return calculateCacheSize(mRGBACacheTextures);
-        }
-        default: {
-            return 0;
+static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
+    uint32_t size = 0;
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = cacheTextures[i];
+        if (cacheTexture && cacheTexture->getPixelBuffer()) {
+            size += cacheTexture->calculateFreeMemory();
         }
     }
+    return size;
+}
+
+const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const {
+    switch (format) {
+        case GL_ALPHA: {
+            return mACacheTextures;
+        }
+        case GL_RGBA: {
+            return mRGBACacheTextures;
+        }
+        default: {
+            LOG_ALWAYS_FATAL("Unsupported format: %d", format);
+            // Impossible to hit this, but the compiler doesn't know that
+            return *(new std::vector<CacheTexture*>());
+        }
+    }
+}
+
+static void dumpTextures(String8& log, const char* tag,
+        const std::vector<CacheTexture*>& cacheTextures) {
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = cacheTextures[i];
+        if (cacheTexture && cacheTexture->getPixelBuffer()) {
+            uint32_t free = cacheTexture->calculateFreeMemory();
+            uint32_t total = cacheTexture->getPixelBuffer()->getSize();
+            log.appendFormat("    %-4s texture %d     %8d / %8d\n", tag, i, total - free, total);
+        }
+    }
+}
+
+void FontRenderer::dumpMemoryUsage(String8& log) const {
+    const uint32_t sizeA8 = getCacheSize(GL_ALPHA);
+    const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA);
+    const uint32_t sizeRGBA = getCacheSize(GL_RGBA);
+    const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA);
+    log.appendFormat("  FontRenderer A8      %8d / %8d\n", usedA8, sizeA8);
+    dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA));
+    log.appendFormat("  FontRenderer RGBA    %8d / %8d\n", usedRGBA, sizeRGBA);
+    dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA));
+    log.appendFormat("  FontRenderer total   %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA);
+}
+
+uint32_t FontRenderer::getCacheSize(GLenum format) const {
+    return calculateCacheSize(cacheTexturesForFormat(format));
+}
+
+uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
+    return calculateFreeCacheSize(cacheTexturesForFormat(format));
+}
+
+uint32_t FontRenderer::getSize() const {
+    return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 578beaa..e836c20 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -20,8 +20,12 @@
 #include "font/CacheTexture.h"
 #include "font/CachedGlyphInfo.h"
 #include "font/Font.h"
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+#include "font/FontCacheHistoryTracker.h"
+#endif
 
 #include <utils/LruCache.h>
+#include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
 #include <SkPaint.h>
@@ -117,7 +121,12 @@
         mLinearFiltering = linearFiltering;
     }
 
-    uint32_t getCacheSize(GLenum format) const;
+    uint32_t getSize() const;
+    void dumpMemoryUsage(String8& log) const;
+
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+    FontCacheHistoryTracker& historyTracker() { return mHistoryTracker; }
+#endif
 
 private:
     friend class Font;
@@ -160,6 +169,10 @@
         mUploadTexture = true;
     }
 
+    const std::vector<CacheTexture*>& cacheTexturesForFormat(GLenum format) const;
+    uint32_t getCacheSize(GLenum format) const;
+    uint32_t getFreeCacheSize(GLenum format) const;
+
     uint32_t mSmallCacheWidth;
     uint32_t mSmallCacheHeight;
     uint32_t mLargeCacheWidth;
@@ -184,6 +197,10 @@
 
     bool mLinearFiltering;
 
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+    FontCacheHistoryTracker mHistoryTracker;
+#endif
+
 #ifdef ANDROID_ENABLE_RENDERSCRIPT
     // RS constructs
     RSC::sp<RSC::RS> mRs;
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 5813e7f..bd27a1a 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -22,6 +22,8 @@
 
 #include <SkPaint.h>
 
+#include <utils/String8.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -46,8 +48,16 @@
         return *mRenderer;
     }
 
-    uint32_t getFontRendererSize(GLenum format) const {
-        return mRenderer ? mRenderer->getCacheSize(format) : 0;
+    void dumpMemoryUsage(String8& log) const {
+        if (mRenderer) {
+            mRenderer->dumpMemoryUsage(log);
+        } else {
+            log.appendFormat("FontRenderer doesn't exist.\n");
+        }
+    }
+
+    uint32_t getSize() const {
+        return mRenderer ? mRenderer->getSize() : 0;
     }
 
     void endPrecaching();
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 09775496..9553ab4 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -26,6 +26,7 @@
 #include <SkDrawFilter.h>
 #include <SkGraphics.h>
 #include <SkImage.h>
+#include <SkImagePriv.h>
 #include <SkRSXform.h>
 #include <SkShader.h>
 #include <SkTemplates.h>
@@ -179,9 +180,10 @@
 static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) {
     SkCanvas::SaveLayerFlags layerFlags = 0;
 
-    if (!(flags & SaveFlags::HasAlphaLayer)) {
-        layerFlags |= SkCanvas::kIsOpaque_SaveLayerFlag;
-    }
+    // We intentionally ignore the SaveFlags::HasAlphaLayer and
+    // SkCanvas::kIsOpaque_SaveLayerFlag flags because HWUI ignores it
+    // and our Android client may use it incorrectly.
+    // In Skia, this flag is purely for performance optimization.
 
     if (!(flags & SaveFlags::ClipToLayer)) {
         layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag;
@@ -593,10 +595,13 @@
     if (paint) {
         tmpPaint = *paint;
     }
-    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
-                                                    SkShader::kClamp_TileMode,
-                                                    SkShader::kClamp_TileMode);
-    SkSafeUnref(tmpPaint.setShader(shader));
+    sk_sp<SkShader> shader = SkMakeBitmapShader(bitmap,
+                                                SkShader::kClamp_TileMode,
+                                                SkShader::kClamp_TileMode,
+                                                nullptr,
+                                                kNever_SkCopyPixelsMode,
+                                                nullptr);
+    tmpPaint.setShader(std::move(shader));
 
     mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
                          texs, (const SkColor*)colors, NULL, indices,
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index fcdde45..49e9f65 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -330,5 +330,17 @@
     return false;
 }
 
+uint32_t CacheTexture::calculateFreeMemory() const {
+    CacheBlock* cacheBlock = mCacheBlocks;
+    uint32_t free = 0;
+    // currently only two formats are supported: GL_ALPHA or GL_RGBA;
+    uint32_t bpp = mFormat == GL_RGBA ? 4 : 1;
+    while (cacheBlock) {
+        free += bpp * cacheBlock->mWidth * cacheBlock->mHeight;
+        cacheBlock = cacheBlock->mNext;
+    }
+    return free;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 4dfb41d..6750a8a 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -178,6 +178,8 @@
         return mCurrentQuad == mMaxQuadCount;
     }
 
+    uint32_t calculateFreeMemory() const;
+
 private:
     void setDirty(bool dirty);
 
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 9c812bc..24d497c 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -408,9 +408,15 @@
         if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
             int penX = x + (int) roundf(positions[(glyphsCount << 1)]);
             int penY = y + (int) roundf(positions[(glyphsCount << 1) + 1]);
-
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+            mState->historyTracker().glyphRendered(cachedGlyph, penX, penY);
+#endif
             (*this.*render)(cachedGlyph, penX, penY,
                     bitmap, bitmapW, bitmapH, bounds, positions);
+        } else {
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+            mState->historyTracker().glyphRendered(cachedGlyph, -1, -1);
+#endif
         }
 
         glyphsCount++;
diff --git a/libs/hwui/font/FontCacheHistoryTracker.cpp b/libs/hwui/font/FontCacheHistoryTracker.cpp
new file mode 100644
index 0000000..a2bfb27
--- /dev/null
+++ b/libs/hwui/font/FontCacheHistoryTracker.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 "FontCacheHistoryTracker.h"
+
+#include "CachedGlyphInfo.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+void FontCacheHistoryTracker::dumpCachedGlyph(String8& log, const CachedGlyph& glyph) {
+    log.appendFormat("glyph (texture %p, position: (%d, %d), size: %dx%d, gen: %d)", glyph.texture,
+            glyph.startX, glyph.startY, glyph.bitmapW, glyph.bitmapH, glyph.generation);
+}
+
+void FontCacheHistoryTracker::dumpRenderEntry(String8& log, const RenderEntry& entry) {
+    if (entry.penX == -1 && entry.penY == -1) {
+        log.appendFormat("      glyph skipped in gen: %d\n", entry.glyph.generation);
+    } else {
+        log.appendFormat("      rendered ");
+        dumpCachedGlyph(log, entry.glyph);
+        log.appendFormat(" at (%d, %d)\n", entry.penX, entry.penY);
+    }
+}
+
+void FontCacheHistoryTracker::dumpUploadEntry(String8& log, const CachedGlyph& glyph) {
+    if (glyph.bitmapW == 0 && glyph.bitmapH == 0) {
+        log.appendFormat("      cleared cachetexture %p in gen %d\n", glyph.texture,
+                glyph.generation);
+    } else {
+        log.appendFormat("      uploaded ");
+        dumpCachedGlyph(log, glyph);
+        log.appendFormat("\n");
+    }
+}
+
+void FontCacheHistoryTracker::dump(String8& log) const {
+    log.appendFormat("FontCacheHistory: \n");
+    log.appendFormat("  Upload history: \n");
+    for (size_t i = 0; i < mUploadHistory.size(); i++) {
+        dumpUploadEntry(log, mUploadHistory[i]);
+    }
+    log.appendFormat("  Render history: \n");
+    for (size_t i = 0; i < mRenderHistory.size(); i++) {
+        dumpRenderEntry(log, mRenderHistory[i]);
+    }
+}
+
+void FontCacheHistoryTracker::glyphRendered(CachedGlyphInfo* glyphInfo, int penX, int penY) {
+    RenderEntry& entry = mRenderHistory.next();
+    entry.glyph.generation = generation;
+    entry.glyph.texture = glyphInfo->mCacheTexture;
+    entry.glyph.startX = glyphInfo->mStartX;
+    entry.glyph.startY = glyphInfo->mStartY;
+    entry.glyph.bitmapW = glyphInfo->mBitmapWidth;
+    entry.glyph.bitmapH = glyphInfo->mBitmapHeight;
+    entry.penX = penX;
+    entry.penY = penY;
+}
+
+void FontCacheHistoryTracker::glyphUploaded(CacheTexture* texture, uint32_t x, uint32_t y,
+        uint16_t glyphW, uint16_t glyphH) {
+    CachedGlyph& glyph = mUploadHistory.next();
+    glyph.generation = generation;
+    glyph.texture = texture;
+    glyph.startX = x;
+    glyph.startY = y;
+    glyph.bitmapW = glyphW;
+    glyph.bitmapH = glyphH;
+}
+
+void FontCacheHistoryTracker::glyphsCleared(CacheTexture* texture) {
+    CachedGlyph& glyph = mUploadHistory.next();
+    glyph.generation = generation;
+    glyph.texture = texture;
+    glyph.startX = 0;
+    glyph.startY = 0;
+    glyph.bitmapW = 0;
+    glyph.bitmapH = 0;
+}
+
+void FontCacheHistoryTracker::frameCompleted() {
+    generation++;
+}
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/FontCacheHistoryTracker.h b/libs/hwui/font/FontCacheHistoryTracker.h
new file mode 100644
index 0000000..f1d9b9f
--- /dev/null
+++ b/libs/hwui/font/FontCacheHistoryTracker.h
@@ -0,0 +1,64 @@
+/*
+ * 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 "../utils/RingBuffer.h"
+
+#include <utils/String8.h>
+
+namespace android {
+namespace uirenderer {
+
+class CacheTexture;
+struct CachedGlyphInfo;
+
+// Tracks glyph uploads and recent rendered/skipped glyphs, so it can give an idea
+// what a missing character is: skipped glyph, wrong coordinates in cache texture etc.
+class FontCacheHistoryTracker {
+public:
+    void glyphRendered(CachedGlyphInfo*, int penX, int penY);
+    void glyphUploaded(CacheTexture*, uint32_t x, uint32_t y, uint16_t glyphW, uint16_t glyphH);
+    void glyphsCleared(CacheTexture*);
+    void frameCompleted();
+
+    void dump(String8& log) const;
+private:
+    struct CachedGlyph {
+        void* texture;
+        uint16_t generation;
+        uint16_t startX;
+        uint16_t startY;
+        uint16_t bitmapW;
+        uint16_t bitmapH;
+    };
+
+    struct RenderEntry {
+        CachedGlyph glyph;
+        int penX;
+        int penY;
+    };
+
+    static void dumpCachedGlyph(String8& log, const CachedGlyph& glyph);
+    static void dumpRenderEntry(String8& log, const RenderEntry& entry);
+    static void dumpUploadEntry(String8& log, const CachedGlyph& glyph);
+
+    RingBuffer<RenderEntry, 300> mRenderHistory;
+    RingBuffer<CachedGlyph, 120> mUploadHistory;
+    uint16_t generation = 0;
+};
+
+}; // namespace uirenderer
+}; // namespace android
\ No newline at end of file
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 289a72f..43471e5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -412,6 +412,11 @@
     }
 
     GpuMemoryTracker::onFrameCompleted();
+#ifdef BUGREPORT_FONT_CACHE_USAGE
+    Caches& caches = Caches::getInstance();
+    caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
+#endif
+
 }
 
 // Called by choreographer to do an RT-driven animation
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index c006d99..f3b7b51 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -117,6 +117,7 @@
 
     std::unique_ptr<TestScene> scene(info.createScene(opts));
 
+    Properties::forceDrawFrame = true;
     TestContext testContext;
     testContext.setRenderOffscreen(opts.renderOffscreen);
 
diff --git a/libs/hwui/tests/scripts/prep_buller.sh b/libs/hwui/tests/scripts/prep_buller.sh
index b2f68bd..65292c3 100755
--- a/libs/hwui/tests/scripts/prep_buller.sh
+++ b/libs/hwui/tests/scripts/prep_buller.sh
@@ -38,12 +38,27 @@
 adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
 adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
 
-#0 762 1144 1525 2288 3509 4173 5271 5928 7904 9887 11863
+# angler: 0 762 1144 1525 2288 3509 4173 5271 5928 7904 9887 11863
 adb shell "echo 11863 > /sys/class/devfreq/qcom,gpubw.70/min_freq" &> /dev/null
-adb shell "echo 11863 > /sys/class/devfreq/qcom,gpubw.19/min_freq" &> /dev/null
+# bullhead: 0 762 1144 1525 2288 3509 4173 5271 5928 7102
+adb shell "echo 7102 > /sys/class/devfreq/qcom,gpubw.19/min_freq" &> /dev/null
 
-#600000000 510000000 450000000 390000000 305000000 180000000
-echo "performance mode, 305 MHz"
+
+board=$(adb shell "getprop ro.product.board")
+freq=0
+if [ "$board" = "bullhead" ]
+then
+    #600000000 490000000 450000000 367000000 300000000 180000000
+    freq=300000000
+else
+    #600000000 510000000 450000000 390000000 305000000 180000000
+    freq=305000000
+fi
+echo "performance mode, $freq Hz"
 adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
-adb shell "echo 305000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
-adb shell "echo 305000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
+adb shell "echo $freq > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
+adb shell "echo $freq > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
+
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel"
+
diff --git a/libs/hwui/tests/scripts/stopruntime.sh b/libs/hwui/tests/scripts/stopruntime.sh
old mode 100644
new mode 100755
index bb8fcb6..85a91db
--- a/libs/hwui/tests/scripts/stopruntime.sh
+++ b/libs/hwui/tests/scripts/stopruntime.sh
@@ -22,6 +22,5 @@
     adb shell kill $pid
 done
 adb shell setprop debug.egl.traceGpuCompletion 1
+adb shell setprop debug.sf.nobootanimation 1
 adb shell daemonize surfaceflinger
-sleep 3
-adb shell setprop service.bootanim.exit 1
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index fdf0b54..edc7191 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -740,10 +740,13 @@
     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         SkPaint paint;
-        SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
+        sk_sp<SkShader> shader = SkMakeBitmapShader(bitmap,
                 SkShader::TileMode::kClamp_TileMode,
-                SkShader::TileMode::kClamp_TileMode));
-        paint.setShader(shader);
+                SkShader::TileMode::kClamp_TileMode,
+                nullptr,
+                kNever_SkCopyPixelsMode,
+                nullptr);
+        paint.setShader(std::move(shader));
         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
     });
     auto& bitmaps = dl->getBitmapResources();
@@ -754,21 +757,24 @@
     SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         SkPaint paint;
-        SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
+        sk_sp<SkShader> shader1 = SkMakeBitmapShader(bitmap,
                 SkShader::TileMode::kClamp_TileMode,
-                SkShader::TileMode::kClamp_TileMode));
+                SkShader::TileMode::kClamp_TileMode,
+                nullptr,
+                kNever_SkCopyPixelsMode,
+                nullptr);
 
         SkPoint center;
         center.set(50, 50);
         SkColor colors[2];
         colors[0] = Color::Black;
         colors[1] = Color::White;
-        SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
-                SkShader::TileMode::kRepeat_TileMode));
+        sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(center, 50, colors, nullptr, 2,
+                SkShader::TileMode::kRepeat_TileMode);
 
-        SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
-                SkXfermode::Mode::kMultiply_Mode));
-        paint.setShader(composeShader);
+        sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(std::move(shader1), std::move(shader2),
+                SkXfermode::Mode::kMultiply_Mode);
+        paint.setShader(std::move(composeShader));
         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
     });
     auto& bitmaps = dl->getBitmapResources();
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index cd759cb..a30ada0 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -17,8 +17,9 @@
 #include "tests/common/TestUtils.h"
 
 #include <gtest/gtest.h>
-#include <SkShader.h>
 #include <SkColorMatrixFilter.h>
+#include <SkImagePriv.h>
+#include <SkShader.h>
 
 using namespace android;
 using namespace android::uirenderer;
@@ -29,10 +30,13 @@
  */
 TEST(SkiaBehavior, CreateBitmapShader1x1) {
     SkBitmap origBitmap = TestUtils::createSkBitmap(1, 1);
-    SkAutoTUnref<SkShader> s(SkShader::CreateBitmapShader(
+    sk_sp<SkShader> s = SkMakeBitmapShader(
             origBitmap,
             SkShader::kClamp_TileMode,
-            SkShader::kRepeat_TileMode));
+            SkShader::kRepeat_TileMode,
+            nullptr,
+            kNever_SkCopyPixelsMode,
+            nullptr);
 
     SkBitmap bitmap;
     SkShader::TileMode xy[2];
@@ -68,3 +72,13 @@
         EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr));
     }
 }
+
+TEST(SkiaBehavior, porterDuffCreateIsCached) {
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kOverlay_Mode);
+    auto expected = paint.getXfermode();
+    paint.setXfermodeMode(SkXfermode::kClear_Mode);
+    ASSERT_NE(expected, paint.getXfermode());
+    paint.setXfermodeMode(SkXfermode::kOverlay_Mode);
+    ASSERT_EQ(expected, paint.getXfermode());
+}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 87a22ce..89b4fb2 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -36,6 +36,29 @@
 
 namespace android {
 
+// --- WeakLooperCallback ---
+
+class WeakLooperCallback: public LooperCallback {
+protected:
+    virtual ~WeakLooperCallback() { }
+
+public:
+    WeakLooperCallback(const wp<LooperCallback>& callback) :
+        mCallback(callback) {
+    }
+
+    virtual int handleEvent(int fd, int events, void* data) {
+        sp<LooperCallback> callback = mCallback.promote();
+        if (callback != NULL) {
+            return callback->handleEvent(fd, events, data);
+        }
+        return 0; // the client is gone, remove the callback
+    }
+
+private:
+    wp<LooperCallback> mCallback;
+};
+
 // --- PointerController ---
 
 // Time to wait before starting the fade when the pointer is inactive.
@@ -57,10 +80,11 @@
         const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
         mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
     mHandler = new WeakMessageHandler(this);
+    mCallback = new WeakLooperCallback(this);
 
     if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
         mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
-                       Looper::EVENT_INPUT, this, nullptr);
+                       Looper::EVENT_INPUT, mCallback, nullptr);
     } else {
         ALOGE("Failed to initialize DisplayEventReceiver.");
     }
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 99292d7..4794f3d 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -144,6 +144,7 @@
     sp<Looper> mLooper;
     sp<SpriteController> mSpriteController;
     sp<WeakMessageHandler> mHandler;
+    sp<LooperCallback> mCallback;
 
     DisplayEventReceiver mDisplayEventReceiver;
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 5286f8f..89709ee 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -24,6 +24,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -170,6 +171,66 @@
     public final static int USAGE_VIRTUAL_SOURCE = 15;
 
     /**
+     * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
+     *            if applicable.
+     */
+
+    /**
+     * @hide
+     * Denotes a usage for notifications that do not expect immediate intervention from the user,
+     * will be muted when the Zen mode disables notifications
+     * @see #SUPPRESSIBLE_USAGES
+     */
+    public final static int SUPPRESSIBLE_NOTIFICATION = 1;
+    /**
+     * @hide
+     * Denotes a usage for notifications that do expect immediate intervention from the user,
+     * will be muted when the Zen mode disables calls
+     * @see #SUPPRESSIBLE_USAGES
+     */
+    public final static int SUPPRESSIBLE_CALL = 2;
+
+    /**
+     * @hide
+     * Array of all usage types for calls and notifications to assign the suppression behavior,
+     * used by the Zen mode restrictions.
+     * @see com.android.server.notification.ZenModeHelper
+     */
+    public static final SparseIntArray SUPPRESSIBLE_USAGES;
+
+    static {
+        SUPPRESSIBLE_USAGES = new SparseIntArray();
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION,                      SUPPRESSIBLE_NOTIFICATION);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_RINGTONE,             SUPPRESSIBLE_CALL);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_COMMUNICATION_REQUEST,SUPPRESSIBLE_CALL);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_COMMUNICATION_INSTANT,SUPPRESSIBLE_NOTIFICATION);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_COMMUNICATION_DELAYED,SUPPRESSIBLE_NOTIFICATION);
+        SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION_EVENT,                SUPPRESSIBLE_NOTIFICATION);
+    }
+
+    /**
+     * @hide
+     * Array of all usage types exposed in the SDK that applications can use.
+     */
+    public final static int[] SDK_USAGES = {
+            USAGE_UNKNOWN,
+            USAGE_MEDIA,
+            USAGE_VOICE_COMMUNICATION,
+            USAGE_VOICE_COMMUNICATION_SIGNALLING,
+            USAGE_ALARM,
+            USAGE_NOTIFICATION,
+            USAGE_NOTIFICATION_RINGTONE,
+            USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+            USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+            USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+            USAGE_NOTIFICATION_EVENT,
+            USAGE_ASSISTANCE_ACCESSIBILITY,
+            USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+            USAGE_ASSISTANCE_SONIFICATION,
+            USAGE_GAME
+    };
+
+    /**
      * Flag defining a behavior where the audibility of the sound will be ensured by the system.
      */
     public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 2cbeb3a2..ed71849 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -241,6 +241,10 @@
      * same {@link Surface} can be reused with a different API once the first source is
      * disconnected from the {@link Surface}.</p>
      *
+     * <p>Please note that holding on to the Surface object returned by this method is not enough
+     * to keep its parent ImageReader from being reclaimed. In that sense, a Surface acts like a
+     * {@link java.lang.ref.WeakReference weak reference} to the ImageReader that provides it.</p>
+     *
      * @return A {@link Surface} to use for a drawing target for various APIs.
      */
     public Surface getSurface() {
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9e560d5..4d332a8 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -510,6 +510,11 @@
     /**
      * Advance to the next sample. Returns false if no more sample data
      * is available (end of stream).
+     *
+     * When extracting a local file, the behaviors of {@link #advance} and
+     * {@link #readSampleData} are undefined in presence of concurrent
+     * writes to the same local file; more specifically, end of stream
+     * could be signalled earlier than expected.
      */
     public native boolean advance();
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index dd26799..bf1559d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1641,7 +1641,8 @@
      * (i.e. reaches the end of the stream).
      * The media framework will attempt to transition from this player to
      * the next as seamlessly as possible. The next player can be set at
-     * any time before completion. The next player must be prepared by the
+     * any time before completion, but shall be after setDataSource has been
+     * called successfully. The next player must be prepared by the
      * app, and the application should not call start() on it.
      * The next MediaPlayer must be different from 'this'. An exception
      * will be thrown if next == this.
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 5b4443b..96ea834 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -219,26 +219,12 @@
             }
 
             if (mBluetoothA2dpRoute != null) {
-                final boolean a2dpEnabled = isBluetoothA2dpOn();
-                if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
-                        mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
-                    selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
-                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
-                        a2dpEnabled) {
+                if (mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) {
                     selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
                 }
             }
         }
 
-        boolean isBluetoothA2dpOn() {
-            try {
-                return mAudioService.isBluetoothA2dpOn();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
-                return false;
-            }
-        }
-
         void updateDiscoveryRequest() {
             // What are we looking for today?
             int routeTypes = 0;
@@ -907,6 +893,11 @@
     static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) {
         Log.v(TAG, "Selecting route: " + route);
         assert(route != null);
+        if (route == sStatic.mDefaultAudioVideo && sStatic.mBluetoothA2dpRoute != null) {
+            Log.i(TAG, "Change the route to a BT route: " + sStatic.mBluetoothA2dpRoute
+                    + "\nDo not select the default route when a BT route is available.");
+            route = sStatic.mBluetoothA2dpRoute;
+        }
         final RouteInfo oldRoute = sStatic.mSelectedRoute;
         if (oldRoute == route) return;
         if (!route.matchesTypes(types)) {
@@ -916,16 +907,6 @@
             return;
         }
 
-        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
-        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
-                (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
-            try {
-                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
-            }
-        }
-
         final WifiDisplay activeDisplay =
                 sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
         final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
@@ -965,7 +946,7 @@
     static void selectDefaultRouteStatic() {
         // TODO: Be smarter about the route types here; this selects for all valid.
         if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
-                && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
+                && sStatic.mBluetoothA2dpRoute != null) {
             selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
         } else {
             selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
@@ -1297,12 +1278,7 @@
                 selectedRoute == sStatic.mDefaultAudioVideo) {
             dispatchRouteVolumeChanged(selectedRoute);
         } else if (sStatic.mBluetoothA2dpRoute != null) {
-            try {
-                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
-                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
-            }
+            dispatchRouteVolumeChanged(sStatic.mBluetoothA2dpRoute);
         } else {
             dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
         }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index f6e2a0c..ddfa025 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -21,12 +21,14 @@
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.drm.DrmManagerClient;
 import android.graphics.BitmapFactory;
 import android.mtp.MtpConstants;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -153,6 +155,11 @@
     private static final String MUSIC_DIR = "/music/";
     private static final String PODCAST_DIR = "/podcasts/";
 
+    public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
+    public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
+    private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
+    private static String sLastInternalScanFingerprint;
+
     private static final String[] ID3_GENRES = {
         // ID3v1 Genres
         "Blues",
@@ -402,6 +409,13 @@
         mMediaProvider = mContext.getContentResolver()
                 .acquireContentProviderClient(MediaStore.AUTHORITY);
 
+        if (sLastInternalScanFingerprint == null) {
+            final SharedPreferences scanSettings =
+                    mContext.getSharedPreferences(SCANNED_BUILD_PREFS_NAME, Context.MODE_PRIVATE);
+            sLastInternalScanFingerprint =
+                    scanSettings.getString(LAST_INTERNAL_SCAN_FINGERPRINT, new String());
+        }
+
         mAudioUri = Audio.Media.getContentUri(volumeName);
         mVideoUri = Video.Media.getContentUri(volumeName);
         mImagesUri = Images.Media.getContentUri(volumeName);
@@ -585,16 +599,24 @@
                     entry.mRowId = 0;
                 }
 
-                if (entry.mPath != null &&
-                        ((!mDefaultNotificationSet &&
+                if (entry.mPath != null) {
+                    if (((!mDefaultNotificationSet &&
                                 doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
                         || (!mDefaultRingtoneSet &&
                                 doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
                         || (!mDefaultAlarmSet &&
                                 doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
-                    Log.w(TAG, "forcing rescan of " + entry.mPath +
-                            "since ringtone setting didn't finish");
-                    scanAlways = true;
+                        Log.w(TAG, "forcing rescan of " + entry.mPath +
+                                "since ringtone setting didn't finish");
+                        scanAlways = true;
+                    } else if (isSystemSoundWithMetadata(entry.mPath)
+                            && !Build.FINGERPRINT.equals(sLastInternalScanFingerprint)) {
+                        // file is located on the system partition where the date cannot be trusted:
+                        // rescan if the build fingerprint has changed since the last scan.
+                        Log.i(TAG, "forcing rescan of " + entry.mPath
+                                + " since build fingerprint changed");
+                        scanAlways = true;
+                    }
                 }
 
                 // rescan for metadata if file was modified since last scan
@@ -1128,6 +1150,15 @@
 
     }; // end of anonymous MediaScannerClient instance
 
+    private static boolean isSystemSoundWithMetadata(String path) {
+        if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
+                || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
+                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
+            return true;
+        }
+        return false;
+    }
+
     private String settingSetIndicatorName(String base) {
         return base + "_set";
     }
@@ -1252,16 +1283,6 @@
         }
     }
 
-    private boolean inScanDirectory(String path, String[] directories) {
-        for (int i = 0; i < directories.length; i++) {
-            String directory = directories[i];
-            if (path.startsWith(directory)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void pruneDeadThumbnailFiles() {
         HashSet<String> existingFiles = new HashSet<String>();
         String directory = "/sdcard/DCIM/.thumbnails";
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
index d5115de..fbd3510 100644
--- a/media/java/android/media/midi/IMidiDeviceServer.aidl
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -17,19 +17,18 @@
 package android.media.midi;
 
 import android.media.midi.MidiDeviceInfo;
-import android.os.ParcelFileDescriptor;
 
 /** @hide */
 interface IMidiDeviceServer
 {
-    ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
-    ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
+    FileDescriptor openInputPort(IBinder token, int portNumber);
+    FileDescriptor openOutputPort(IBinder token, int portNumber);
     void closePort(IBinder token);
     void closeDevice();
 
     // connects the input port pfd to the specified output port
     // Returns the PID of the called process.
-    int connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
+    int connectPorts(IBinder token, in FileDescriptor fd, int outputPortNumber);
 
     MidiDeviceInfo getDeviceInfo();
     void setDeviceInfo(in MidiDeviceInfo deviceInfo);
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
index da44ca6..f79cd06 100644
--- a/media/java/android/media/midi/MidiDevice.java
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -18,7 +18,6 @@
 
 import android.os.Binder;
 import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
@@ -28,6 +27,7 @@
 import libcore.io.IoUtils;
 
 import java.io.Closeable;
+import java.io.FileDescriptor;
 import java.io.IOException;
 
 /**
@@ -129,11 +129,11 @@
         }
         try {
             IBinder token = new Binder();
-            ParcelFileDescriptor pfd = mDeviceServer.openInputPort(token, portNumber);
-            if (pfd == null) {
+            FileDescriptor fd = mDeviceServer.openInputPort(token, portNumber);
+            if (fd == null) {
                 return null;
             }
-            return new MidiInputPort(mDeviceServer, token, pfd, portNumber);
+            return new MidiInputPort(mDeviceServer, token, fd, portNumber);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in openInputPort");
             return null;
@@ -155,11 +155,11 @@
         }
         try {
             IBinder token = new Binder();
-            ParcelFileDescriptor pfd = mDeviceServer.openOutputPort(token, portNumber);
-            if (pfd == null) {
+            FileDescriptor fd = mDeviceServer.openOutputPort(token, portNumber);
+            if (fd == null) {
                 return null;
             }
-            return new MidiOutputPort(mDeviceServer, token, pfd, portNumber);
+            return new MidiOutputPort(mDeviceServer, token, fd, portNumber);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in openOutputPort");
             return null;
@@ -186,20 +186,20 @@
             return null;
         }
 
-        ParcelFileDescriptor pfd = inputPort.claimFileDescriptor();
-        if (pfd == null) {
+        FileDescriptor fd = inputPort.claimFileDescriptor();
+        if (fd == null) {
             return null;
         }
         try {
             IBinder token = new Binder();
-            int calleePid = mDeviceServer.connectPorts(token, pfd, outputPortNumber);
-            // If the service is a different Process then it will duplicate the pfd
+            int calleePid = mDeviceServer.connectPorts(token, fd, outputPortNumber);
+            // If the service is a different Process then it will duplicate the fd
             // and we can safely close this one.
-            // But if the service is in the same Process then closing the pfd will
+            // But if the service is in the same Process then closing the fd will
             // kill the connection. So don't do that.
             if (calleePid != Process.myPid()) {
                 // close our copy of the file descriptor
-                IoUtils.closeQuietly(pfd);
+                IoUtils.closeQuietly(fd);
             }
 
             return new MidiConnection(token, inputPort);
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index 4c49f67..eaa8654 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -18,9 +18,10 @@
 
 import android.os.Binder;
 import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.system.OsConstants;
 import android.util.Log;
 
@@ -31,6 +32,7 @@
 import libcore.io.IoUtils;
 
 import java.io.Closeable;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -167,11 +169,22 @@
         }
     }
 
+    private static FileDescriptor[] createSeqPacketSocketPair() throws IOException {
+        try {
+            final FileDescriptor fd0 = new FileDescriptor();
+            final FileDescriptor fd1 = new FileDescriptor();
+            Os.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0, fd0, fd1);
+            return new FileDescriptor[] { fd0, fd1 };
+        } catch (ErrnoException e) {
+            throw e.rethrowAsIOException();
+        }
+    }
+
     // Binder interface stub for receiving connection requests from clients
     private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() {
 
         @Override
-        public ParcelFileDescriptor openInputPort(IBinder token, int portNumber) {
+        public FileDescriptor openInputPort(IBinder token, int portNumber) {
             if (mDeviceInfo.isPrivate()) {
                 if (Binder.getCallingUid() != Process.myUid()) {
                     throw new SecurityException("Can't access private device from different UID");
@@ -190,8 +203,7 @@
                 }
 
                 try {
-                    ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
-                                                        OsConstants.SOCK_SEQPACKET);
+                    FileDescriptor[] pair = createSeqPacketSocketPair();
                     MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber);
                     mInputPortOutputPorts[portNumber] = outputPort;
                     outputPort.connect(mInputPortReceivers[portNumber]);
@@ -203,14 +215,14 @@
                     updateDeviceStatus();
                     return pair[1];
                 } catch (IOException e) {
-                    Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort");
+                    Log.e(TAG, "unable to create FileDescriptors in openInputPort");
                     return null;
                 }
             }
         }
 
         @Override
-        public ParcelFileDescriptor openOutputPort(IBinder token, int portNumber) {
+        public FileDescriptor openOutputPort(IBinder token, int portNumber) {
             if (mDeviceInfo.isPrivate()) {
                 if (Binder.getCallingUid() != Process.myUid()) {
                     throw new SecurityException("Can't access private device from different UID");
@@ -223,14 +235,13 @@
             }
 
             try {
-                ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
-                                                    OsConstants.SOCK_SEQPACKET);
+                FileDescriptor[] pair = createSeqPacketSocketPair();
                 MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
                 // Undo the default blocking-mode of the server-side socket for
                 // physical devices to avoid stalling the Java device handler if
                 // client app code gets stuck inside 'onSend' handler.
                 if (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL) {
-                    IoUtils.setBlocking(pair[0].getFileDescriptor(), false);
+                    IoUtils.setBlocking(pair[0], false);
                 }
                 MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber];
                 synchronized (dispatcher) {
@@ -250,7 +261,7 @@
                 }
                 return pair[1];
             } catch (IOException e) {
-                Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort");
+                Log.e(TAG, "unable to create FileDescriptors in openOutputPort");
                 return null;
             }
         }
@@ -281,9 +292,9 @@
         }
 
         @Override
-        public int connectPorts(IBinder token, ParcelFileDescriptor pfd,
+        public int connectPorts(IBinder token, FileDescriptor fd,
                 int outputPortNumber) {
-            MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
+            MidiInputPort inputPort = new MidiInputPort(fd, outputPortNumber);
             MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber];
             synchronized (dispatcher) {
                 dispatcher.getSender().connect(inputPort);
diff --git a/media/java/android/media/midi/MidiInputPort.java b/media/java/android/media/midi/MidiInputPort.java
index db41b10..98ec779 100644
--- a/media/java/android/media/midi/MidiInputPort.java
+++ b/media/java/android/media/midi/MidiInputPort.java
@@ -17,7 +17,6 @@
 package android.media.midi;
 
 import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -26,6 +25,7 @@
 import libcore.io.IoUtils;
 
 import java.io.Closeable;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
@@ -38,7 +38,7 @@
     private IMidiDeviceServer mDeviceServer;
     private final IBinder mToken;
     private final int mPortNumber;
-    private ParcelFileDescriptor mParcelFileDescriptor;
+    private FileDescriptor mFileDescriptor;
     private FileOutputStream mOutputStream;
 
     private final CloseGuard mGuard = CloseGuard.get();
@@ -48,19 +48,19 @@
     private final byte[] mBuffer = new byte[MidiPortImpl.MAX_PACKET_SIZE];
 
     /* package */ MidiInputPort(IMidiDeviceServer server, IBinder token,
-            ParcelFileDescriptor pfd, int portNumber) {
+            FileDescriptor fd, int portNumber) {
         super(MidiPortImpl.MAX_PACKET_DATA_SIZE);
 
         mDeviceServer = server;
         mToken = token;
-        mParcelFileDescriptor = pfd;
+        mFileDescriptor = fd;
         mPortNumber = portNumber;
-        mOutputStream = new FileOutputStream(pfd.getFileDescriptor());
+        mOutputStream = new FileOutputStream(fd);
         mGuard.open("close");
     }
 
-    /* package */ MidiInputPort(ParcelFileDescriptor pfd, int portNumber) {
-        this(null, null, pfd, portNumber);
+    /* package */ MidiInputPort(FileDescriptor fd, int portNumber) {
+        this(null, null, fd, portNumber);
     }
 
     /**
@@ -102,21 +102,21 @@
     }
 
     // used by MidiDevice.connectInputPort() to connect our socket directly to another device
-    /* package */ ParcelFileDescriptor claimFileDescriptor() {
+    /* package */ FileDescriptor claimFileDescriptor() {
         synchronized (mGuard) {
-            ParcelFileDescriptor pfd;
+            FileDescriptor fd;
             synchronized (mBuffer) {
-                pfd = mParcelFileDescriptor;
-                if (pfd == null) return null;
+                fd = mFileDescriptor;
+                if (fd == null) return null;
                 IoUtils.closeQuietly(mOutputStream);
-                mParcelFileDescriptor = null;
+                mFileDescriptor = null;
                 mOutputStream = null;
             }
 
             // Set mIsClosed = true so we will not call mDeviceServer.closePort() in close().
             // MidiDevice.MidiConnection.close() will do the cleanup instead.
             mIsClosed = true;
-            return pfd;
+            return fd;
         }
     }
 
@@ -136,9 +136,9 @@
             if (mIsClosed) return;
             mGuard.close();
             synchronized (mBuffer) {
-                if (mParcelFileDescriptor != null) {
-                    mParcelFileDescriptor.close();
-                    mParcelFileDescriptor = null;
+                if (mFileDescriptor != null) {
+                    IoUtils.closeQuietly(mFileDescriptor);
+                    mFileDescriptor = null;
                 }
                 if (mOutputStream != null) {
                     mOutputStream.close();
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index 54c31e3..ce0402c 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -28,6 +28,7 @@
 import libcore.io.IoUtils;
 
 import java.io.Closeable;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
 
@@ -91,17 +92,17 @@
     };
 
     /* package */ MidiOutputPort(IMidiDeviceServer server, IBinder token,
-            ParcelFileDescriptor pfd, int portNumber) {
+            FileDescriptor fd, int portNumber) {
         mDeviceServer = server;
         mToken = token;
         mPortNumber = portNumber;
-        mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(new ParcelFileDescriptor(fd));
         mThread.start();
         mGuard.open("close");
     }
 
-    /* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) {
-        this(null, null, pfd, portNumber);
+    /* package */ MidiOutputPort(FileDescriptor fd, int portNumber) {
+        this(null, null, fd, portNumber);
     }
 
     /**
diff --git a/media/java/android/media/midi/MidiPortImpl.java b/media/java/android/media/midi/MidiPortImpl.java
index 16fc214..1cd9ed2 100644
--- a/media/java/android/media/midi/MidiPortImpl.java
+++ b/media/java/android/media/midi/MidiPortImpl.java
@@ -34,7 +34,7 @@
     public static final int PACKET_TYPE_FLUSH = 2;
 
     /**
-     * Maximum size of a packet that can pass through our ParcelFileDescriptor.
+     * Maximum size of a packet that can be passed between processes.
      */
     public static final int MAX_PACKET_SIZE = 1024;
 
@@ -54,7 +54,7 @@
     public static final int MAX_PACKET_DATA_SIZE = MAX_PACKET_SIZE - DATA_PACKET_OVERHEAD;
 
     /**
-     * Utility function for packing MIDI data to be sent through our ParcelFileDescriptor
+     * Utility function for packing MIDI data to be passed between processes
      *
      * message byte array contains variable length MIDI message.
      * messageSize is size of variable length MIDI message
@@ -84,7 +84,7 @@
     }
 
     /**
-     * Utility function for packing a flush command to be sent through our ParcelFileDescriptor
+     * Utility function for packing a flush command to be passed between processes
      */
     public static int packFlush(byte[] dest) {
         dest[0] = PACKET_TYPE_FLUSH;
@@ -99,7 +99,7 @@
     }
 
     /**
-     * Utility function for unpacking MIDI data received from our ParcelFileDescriptor
+     * Utility function for unpacking MIDI data received from other process
      * returns the offset of the MIDI message in packed buffer
      */
     public static int getDataOffset(byte[] buffer, int bufferLength) {
@@ -108,7 +108,7 @@
     }
 
     /**
-     * Utility function for unpacking MIDI data received from our ParcelFileDescriptor
+     * Utility function for unpacking MIDI data received from other process
      * returns size of MIDI data in packed buffer
      */
     public static int getDataSize(byte[] buffer, int bufferLength) {
@@ -117,7 +117,7 @@
     }
 
     /**
-     * Utility function for unpacking MIDI data received from our ParcelFileDescriptor
+     * Utility function for unpacking MIDI data received from other process
      * unpacks timestamp from packed buffer
      */
     public static long getPacketTimestamp(byte[] buffer, int bufferLength) {
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index a215493..4f3314c 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -46,7 +46,7 @@
     void setExtras(in Bundle extras);
     void setRatingType(int type);
     void setRepeatMode(int repeatMode);
-    void setShuffleMode(boolean shuffleMode);
+    void setShuffleModeEnabled(boolean enabled);
 
     // These commands relate to volume handling
     void setPlaybackToLocal(in AudioAttributes attributes);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index b3c6d59..2f6e260 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -47,7 +47,7 @@
     void onSeekTo(long pos);
     void onRate(in Rating rating);
     void onRepeatMode(int repeatMode);
-    void onShuffleMode(boolean shuffleMode);
+    void onShuffleMode(boolean enabled);
     void onCustomAction(String action, in Bundle args);
 
     // These callbacks are for volume handling
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index b73f167..e92758c 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -55,7 +55,7 @@
     Bundle getExtras();
     int getRatingType();
     int getRepeatMode();
-    boolean getShuffleMode();
+    boolean isShuffleModeEnabled();
 
     // These commands are for the TransportControls
     void prepare();
@@ -76,6 +76,6 @@
     void seekTo(long pos);
     void rate(in Rating rating);
     void repeatMode(int repeatMode);
-    void shuffleMode(boolean shuffleMode);
+    void shuffleMode(boolean enabled);
     void sendCustomAction(String action, in Bundle args);
 }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index ddff1b7..8cbf8e1 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -251,15 +251,15 @@
     }
 
     /**
-     * Get the shuffle mode for this session.
+     * Return whether the shuffle mode is enabled for this session.
      *
-     * @return The latest shuffle mode set to the session, or false if not set.
+     * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set.
      */
-    public boolean getShuffleMode() {
+    public boolean isShuffleModeEnabled() {
         try {
-            return mSessionBinder.getShuffleMode();
+            return mSessionBinder.isShuffleModeEnabled();
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getShuffleMode.", e);
+            Log.wtf(TAG, "Error calling isShuffleModeEnabled.", e);
             return false;
         }
     }
@@ -625,10 +625,9 @@
         /**
          * Override to handle changes to the shuffle mode.
          *
-         * @param shuffleMode The shuffle mode. {@code true} if _the_ shuffle mode is on,
-         *                    {@code false} otherwise.
+         * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise.
          */
-        public void onShuffleModeChanged(boolean shuffleMode) {
+        public void onShuffleModeChanged(boolean enabled) {
         }
     }
 
@@ -931,13 +930,13 @@
         /**
          * Set the shuffle mode for this session.
          *
-         * @param shuffleMode {@code true} if the shuffle mode is on, {@code false} otherwise.
+         * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
          */
-        public void setShuffleMode(boolean shuffleMode) {
+        public void setShuffleModeEnabled(boolean enabled) {
             try {
-                mSessionBinder.shuffleMode(shuffleMode);
+                mSessionBinder.shuffleMode(enabled);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling shuffleQueue.", e);
+                Log.wtf(TAG, "Error calling shuffleMode.", e);
             }
         }
 
@@ -1151,10 +1150,10 @@
         }
 
         @Override
-        public void onShuffleModeChanged(boolean shuffleMode) {
+        public void onShuffleModeChanged(boolean enabled) {
             MediaController controller = mController.get();
             if (controller != null) {
-                controller.postMessage(MSG_UPDATE_SHUFFLE_MODE, shuffleMode, null);
+                controller.postMessage(MSG_UPDATE_SHUFFLE_MODE, enabled, null);
             }
         }
     }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index cab5d93..84dc93a 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -501,16 +501,16 @@
     /**
      * Set the shuffle mode for this session.
      * <p>
-     * Note that if this method is not called before, {@link MediaController#getShuffleMode}
+     * Note that if this method is not called before, {@link MediaController#isShuffleModeEnabled}
      * will return {@code false}.
      *
-     * @param shuffleMode {@code true} if the shuffle mode is on, {@code false} otherwise.
+     * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
      */
-    public void setShuffleMode(boolean shuffleMode) {
+    public void setShuffleModeEnabled(boolean enabled) {
         try {
-            mBinder.setShuffleMode(shuffleMode);
+            mBinder.setShuffleModeEnabled(enabled);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error in setShuffleMode.", e);
+            Log.e(TAG, "Error in setShuffleModeEnabled.", e);
         }
     }
 
@@ -637,8 +637,8 @@
         postToCallback(CallbackMessageHandler.MSG_REPEAT_MODE, repeatMode);
     }
 
-    private void dispatchShuffleMode(boolean shuffleMode) {
-        postToCallback(CallbackMessageHandler.MSG_SHUFFLE_MODE, shuffleMode);
+    private void dispatchShuffleMode(boolean enabled) {
+        postToCallback(CallbackMessageHandler.MSG_SHUFFLE_MODE, enabled);
     }
 
     private void dispatchCustomAction(String action, Bundle args) {
@@ -1024,13 +1024,13 @@
         /**
          * Override to handle the setting of the shuffle mode.
          * <p>
-         * You should call {@link #setShuffleMode} before end of this method in order to notify
-         * the change to the {@link MediaController}, or {@link MediaController#getShuffleMode}
-         * could return an invalid value.
+         * You should call {@link #setShuffleModeEnabled} before the end of this method in order to
+         * notify the change to the {@link MediaController}, or
+         * {@link MediaController#isShuffleModeEnabled} could return an invalid value.
          *
-         * @param shuffleMode true if the shuffle mode is on, false otherwise.
+         * @param enabled true when the shuffle mode is enabled, false otherwise.
          */
-        public void onSetShuffleMode(boolean shuffleMode) {
+        public void onSetShuffleModeEnabled(boolean enabled) {
         }
 
         /**
@@ -1223,10 +1223,10 @@
         }
 
         @Override
-        public void onShuffleMode(boolean shuffleMode) {
+        public void onShuffleMode(boolean enabled) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchShuffleMode(shuffleMode);
+                session.dispatchShuffleMode(enabled);
             }
         }
 
@@ -1468,7 +1468,7 @@
                     mCallback.onSetRepeatMode((int) msg.obj);
                     break;
                 case MSG_SHUFFLE_MODE:
-                    mCallback.onSetShuffleMode((boolean) msg.obj);
+                    mCallback.onSetShuffleModeEnabled((boolean) msg.obj);
                     break;
                 case MSG_CUSTOM_ACTION:
                     mCallback.onCustomAction((String) msg.obj, msg.getData());
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 5cdd201..1ea6109 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -46,7 +46,7 @@
             ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
             ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
             ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI,
-            ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE})
+            ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE_ENABLED})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Actions {}
 
@@ -184,11 +184,11 @@
     public static final long ACTION_SET_REPEAT_MODE = 1 << 18;
 
     /**
-     * Indicates this session supports the set shuffle mode command.
+     * Indicates this session supports the set shuffle mode enabled command.
      *
      * @see Builder#setActions(long)
      */
-    public static final long ACTION_SET_SHUFFLE_MODE = 1 << 19;
+    public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 1 << 19;
 
     /**
      * @hide
@@ -467,7 +467,7 @@
      * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
      * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
      * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
-     * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE}</li>
+     * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
      * </ul>
      */
     @Actions
@@ -1003,7 +1003,7 @@
          * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
          * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
          * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
-         * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE}</li>
+         * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
          * </ul>
          *
          * @param actions The set of actions allowed.
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 8184f94..39c1554 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -37,7 +37,7 @@
     size_t mPosition;
 
 public:
-    AssetStream(SkStream* stream);
+    explicit AssetStream(SkStream* stream);
     ~AssetStream();
 
     // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
@@ -60,7 +60,7 @@
     const size_t kMinSizeToRead = 8192;
 
 public:
-    BufferedStream(SkStream* stream);
+    explicit BufferedStream(SkStream* stream);
     ~BufferedStream();
 
     // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
@@ -79,8 +79,8 @@
     size_t mPosition;
 
 public:
-    FileStream(const int fd);
-    FileStream(const String8 filename);
+    explicit FileStream(const int fd);
+    explicit FileStream(const String8 filename);
     ~FileStream();
 
     // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 75a3b6f..0645543 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -435,14 +435,12 @@
 
 // ----------------------------------------------------------------------------
 static void android_media_visualizer_native_release(JNIEnv *env,  jobject thiz) {
-    // ensure that lpVisualizer is deleted before lpJniStorage
-    {
+    { //limit scope so that lpVisualizer is deleted before JNI storage data.
         sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
         if (lpVisualizer == 0) {
             return;
         }
     }
-
     // delete the JNI data
     VisualizerJniStorage* lpJniStorage =
         (VisualizerJniStorage *)env->GetLongField(thiz, fields.fidJniData);
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
index 32e3358..808ec36 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
@@ -102,6 +102,14 @@
         return false;
     }
 
+    public static void registerJobIfNeeded(Context context, long intervalMinutes) {
+        JobScheduler jss = context.getSystemService(JobScheduler.class);
+        JobInfo info = jss.getPendingJob(JOB_ID);
+        if (info == null) {
+            registerJob(context, intervalMinutes);
+        }
+    }
+
     public static void registerJob(Context context, long intervalMinutes) {
         JobScheduler jss = context.getSystemService(JobScheduler.class);
         jss.cancel(JOB_ID);
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
index 8a3ec8f..159b40a 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
@@ -68,6 +68,9 @@
         Tile tile = getQsTile();
         int foodState = mPrefs.getFoodState();
         Food food = new Food(foodState);
+        if (foodState != 0) {
+            NekoService.registerJobIfNeeded(this, food.getInterval(this));
+        }
         tile.setIcon(food.getIcon(this));
         tile.setLabel(food.getName(this));
         tile.setState(foodState != 0 ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
diff --git a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
index 5f54180..bf71b19 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
@@ -43,13 +43,11 @@
     public void addCat(Cat cat) {
         mPrefs.edit()
               .putString(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()), cat.getName())
-              .commit();
+              .apply();
     }
 
     public void removeCat(Cat cat) {
-        mPrefs.edit()
-                .remove(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()))
-                .commit();
+        mPrefs.edit().remove(CAT_KEY_PREFIX + String.valueOf(cat.getSeed())).apply();
     }
 
     public List<Cat> getCats() {
@@ -71,7 +69,7 @@
     }
 
     public void setFoodState(int foodState) {
-        mPrefs.edit().putInt(FOOD_STATE, foodState).commit();
+        mPrefs.edit().putInt(FOOD_STATE, foodState).apply();
     }
 
     public void setListener(PrefsListener listener) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 874eb94..66e1b27 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -111,12 +111,6 @@
 
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
-    /**
-     * Milliseconds after unlocking with fingerprint times out, i.e. the user has to use a
-     * strong auth method like password, PIN or pattern.
-     */
-    private static final long FINGERPRINT_UNLOCK_TIMEOUT_MS = 72 * 60 * 60 * 1000;
-
     // Callback messages
     private static final int MSG_TIME_UPDATE = 301;
     private static final int MSG_BATTERY_UPDATE = 302;
@@ -141,6 +135,7 @@
     private static final int MSG_SERVICE_STATE_CHANGE = 330;
     private static final int MSG_SCREEN_TURNED_ON = 331;
     private static final int MSG_SCREEN_TURNED_OFF = 332;
+    private static final int MSG_DREAMING_STATE_CHANGED = 333;
 
     /** Fingerprint state: Not listening to fingerprint. */
     private static final int FINGERPRINT_STATE_STOPPED = 0;
@@ -293,6 +288,9 @@
                     handleScreenTurnedOff();
                     Trace.endSection();
                     break;
+                case MSG_DREAMING_STATE_CHANGED:
+                    handleDreamingStateChanged(msg.arg1);
+                    break;
             }
         }
     };
@@ -604,7 +602,10 @@
     }
 
     private void scheduleStrongAuthTimeout() {
-        long when = SystemClock.elapsedRealtime() + FINGERPRINT_UNLOCK_TIMEOUT_MS;
+        final DevicePolicyManager dpm =
+                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null,
+                sCurrentUser);
         Intent intent = new Intent(ACTION_STRONG_AUTH_TIMEOUT);
         intent.putExtra(USER_ID, sCurrentUser);
         PendingIntent sender = PendingIntent.getBroadcast(mContext,
@@ -990,6 +991,17 @@
         }
     }
 
+    private void handleDreamingStateChanged(int dreamStart) {
+        final int count = mCallbacks.size();
+        boolean showingDream = dreamStart == 1;
+        for (int i = 0; i < count; i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onDreamingStateChanged(showingDream);
+            }
+        }
+    }
+
     /**
      * IMPORTANT: Must be called from UI thread.
      */
@@ -1739,6 +1751,14 @@
         mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_OFF);
     }
 
+    public void dispatchDreamingStarted() {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_DREAMING_STATE_CHANGED, 1, 0));
+    }
+
+    public void dispatchDreamingStopped() {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_DREAMING_STATE_CHANGED, 0, 0));
+    }
+
     public boolean isDeviceInteractive() {
         return mDeviceInteractive;
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 4a2d356..eb29d9b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -245,4 +245,10 @@
      * Called when the state whether we have a lockscreen wallpaper has changed.
      */
     public void onHasLockscreenWallpaperChanged(boolean hasLockscreenWallpaper) { }
+
+    /**
+     * Called when the dream's window state is changed.
+     * @param dreaming true if the dream's window has been created and is visible
+     */
+    public void onDreamingStateChanged(boolean dreaming) { }
 }
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 1098a8e..ad07283 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -19,3 +19,6 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# For the test package.
+include $(call all-makefiles-under, $(LOCAL_PATH))
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 0e3e0d5..5c577f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -126,6 +126,13 @@
          * internal storage. Key is {@link UserHandle}.
          */
         public SparseLongArray usersSize = new SparseLongArray();
+
+        @Override
+        public String toString() {
+            return "MeasurementDetails: [totalSize: " + totalSize + " availSize: " + availSize
+                    + " cacheSize: " + cacheSize + " mediaSize: " + mediaSize
+                    + " miscSize: " + miscSize + "usersSize: " + usersSize + "]";
+        }
     }
 
     public interface MeasurementReceiver {
@@ -435,7 +442,7 @@
     private static long getDirectorySize(IMediaContainerService imcs, File path) {
         try {
             final long size = imcs.calculateDirectorySize(path.toString());
-            Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
+            if (LOGV) Log.v(TAG, "getDirectorySize(" + path + ") returned " + size);
             return size;
         } catch (Exception e) {
             Log.w(TAG, "Could not read memory from default container service for " + path, e);
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 458672a..05585e53e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -31,7 +31,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 import android.support.v4.widget.DrawerLayout;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
@@ -58,6 +60,7 @@
 
     protected static final boolean DEBUG_TIMING = false;
     private static final String TAG = "SettingsDrawerActivity";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     public static final String EXTRA_SHOW_MENU = "show_drawer_menu";
 
@@ -111,7 +114,7 @@
             public void onItemClick(android.widget.AdapterView<?> parent, View view, int position,
                     long id) {
                 onTileClicked(mDrawerAdapter.getTile(position));
-            };
+            }
         });
 
         mUserManager = UserManager.get(this);
@@ -143,8 +146,16 @@
 
             new CategoriesUpdater().execute();
         }
-        if (getIntent() != null && getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false)) {
-            showMenuIcon();
+        final Intent intent = getIntent();
+        if (intent != null) {
+            if (intent.hasExtra(EXTRA_SHOW_MENU)) {
+                if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
+                    // Intent explicitly set to show menu.
+                    showMenuIcon();
+                }
+            } else if (isTopLevelTile(intent)) {
+                showMenuIcon();
+            }
         }
     }
 
@@ -157,6 +168,30 @@
         super.onPause();
     }
 
+    private boolean isTopLevelTile(Intent intent) {
+        final ComponentName componentName = intent.getComponent();
+        if (componentName == null) {
+            return false;
+        }
+        // Look for a tile that has the same component as incoming intent
+        final List<DashboardCategory> categories = getDashboardCategories();
+        for (DashboardCategory category : categories) {
+            for (Tile tile : category.tiles) {
+                if (TextUtils.equals(tile.intent.getComponent().getClassName(),
+                        componentName.getClassName())) {
+                    if (DEBUG) {
+                        Log.d(TAG, "intent is for top level tile: " + tile.title);
+                    }
+                    return true;
+                }
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Intent is not for top level settings " + intent);
+        }
+        return false;
+    }
+
     public void addCategoryListener(CategoryListener listener) {
         mCategoryListeners.add(listener);
     }
@@ -287,9 +322,9 @@
     private void updateUserHandlesIfNeeded(Tile tile) {
         List<UserHandle> userHandles = tile.userHandle;
 
-        for (int i = userHandles.size()-1; i >= 0; i--) {
+        for (int i = userHandles.size() - 1; i >= 0; i--) {
             if (mUserManager.getUserInfo(userHandles.get(i).getIdentifier()) == null) {
-                if (DEBUG_TIMING) {
+                if (DEBUG) {
                     Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
                 }
                 userHandles.remove(i);
@@ -297,6 +332,11 @@
         }
     }
 
+    @VisibleForTesting
+    public void setUserManager(UserManager userManager) {
+        mUserManager = userManager;
+    }
+
     protected void onTileClicked(Tile tile) {
         if (openTile(tile)) {
             finish();
diff --git a/packages/SettingsLib/tests/Android.mk b/packages/SettingsLib/tests/Android.mk
index d3ffffa..4208522 100644
--- a/packages/SettingsLib/tests/Android.mk
+++ b/packages/SettingsLib/tests/Android.mk
@@ -16,6 +16,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -23,7 +24,10 @@
 
 LOCAL_PACKAGE_NAME := SettingsLibTests
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    espresso-core \
+    mockito-target-minus-junit4
 
 include frameworks/base/packages/SettingsLib/common.mk
 
diff --git a/packages/SettingsLib/tests/AndroidManifest.xml b/packages/SettingsLib/tests/AndroidManifest.xml
index 00d16fc..e6d133b 100644
--- a/packages/SettingsLib/tests/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/AndroidManifest.xml
@@ -17,11 +17,15 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.settingslib">
 
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+
     <application>
         <uses-library android:name="android.test.runner" />
+        <activity android:name=".drawer.SettingsDrawerActivityTest$TestActivity"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.settingslib"
         android:label="Tests for SettingsLib">
     </instrumentation>
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
new file mode 100644
index 0000000..4d7d4cf
--- /dev/null
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.drawer;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.settingslib.drawer.SettingsDrawerActivity;
+import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SettingsDrawerActivityTest {
+    @Mock
+    private UserManager mUserManager;
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class, true, true);
+
+    private static final UserHandle NORMAL_USER = UserHandle.of(1111);
+    private static final UserHandle REMOVED_USER = UserHandle.of(2222);
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        final UserInfo userInfo = new UserInfo(
+                NORMAL_USER.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
+        when(mUserManager.getUserInfo(NORMAL_USER.getIdentifier())).thenReturn(userInfo);
+    }
+
+    @Test
+    public void testUpdateUserHandlesIfNeeded_Normal() {
+        TestActivity activity = mActivityRule.getActivity();
+        activity.setUserManager(mUserManager);
+
+        Tile tile = new Tile();
+        tile.intent = new Intent();
+        tile.userHandle.add(NORMAL_USER);
+
+        activity.openTile(tile);
+
+        assertEquals(tile.userHandle.size(), 1);
+        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
+        verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
+    }
+
+    @Test
+    public void testUpdateUserHandlesIfNeeded_Remove() {
+        TestActivity activity = mActivityRule.getActivity();
+        activity.setUserManager(mUserManager);
+
+        Tile tile = new Tile();
+        tile.intent = new Intent();
+        tile.userHandle.add(REMOVED_USER);
+        tile.userHandle.add(NORMAL_USER);
+        tile.userHandle.add(REMOVED_USER);
+
+        activity.openTile(tile);
+
+        assertEquals(tile.userHandle.size(), 1);
+        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
+        verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
+        verify(mUserManager, times(2)).getUserInfo(REMOVED_USER.getIdentifier());
+    }
+
+    @Test
+    public void startActivityWithNoExtra_showNoHamburgerMenu() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.startActivitySync(new Intent(instrumentation.getTargetContext(),
+                TestActivity.class));
+
+        onView(withContentDescription(R.string.content_description_menu_button))
+                .check(doesNotExist());
+    }
+
+    @Test
+    public void startActivityWithExtraToHideMenu_showNoHamburgerMenu() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+                .putExtra(TestActivity.EXTRA_SHOW_MENU, false);
+        instrumentation.startActivitySync(intent);
+
+        onView(withContentDescription(R.string.content_description_menu_button))
+                .check(doesNotExist());
+    }
+
+    @Test
+    public void startActivityWithExtraToShowMenu_showHamburgerMenu() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+                .putExtra(TestActivity.EXTRA_SHOW_MENU, true);
+        instrumentation.startActivitySync(intent);
+
+        onView(withContentDescription(R.string.content_description_menu_button))
+                .check(matches(isDisplayed()));
+    }
+
+    /**
+     * Test Activity in this test.
+     *
+     * Use this activity because SettingsDrawerActivity hasn't been registered in its
+     * AndroidManifest.xml
+     */
+    public static class TestActivity extends SettingsDrawerActivity {}
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c1e4e2a..672f88d 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -36,7 +36,7 @@
     <fraction name="def_window_transition_scale">100%</fraction>
     <bool name="def_haptic_feedback">true</bool>
 
-    <bool name="def_bluetooth_on">false</bool>
+    <bool name="def_bluetooth_on">true</bool>
     <bool name="def_wifi_display_on">false</bool>
     <bool name="def_install_non_market_apps">false</bool>
     <bool name="def_package_verifier_enable">true</bool>
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 258c82e..71bfe85 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -23,6 +23,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
+    SystemUIPluginLib \
     Keyguard \
     android-support-v7-recyclerview \
     android-support-v7-preference \
@@ -43,9 +44,11 @@
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
-ifneq ($(SYSTEM_UI_INCREMENTAL_BUILDS),)
+ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
     LOCAL_JACK_ENABLED := incremental
+    LOCAL_DX_FLAGS := --multi-dex
+    LOCAL_JACK_FLAGS := --multi-dex native
 endif
 
 include frameworks/base/packages/SettingsLib/common.mk
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3cc16de..8ed1be5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -138,6 +138,9 @@
             android:protectionLevel="signature" />
     <uses-permission android:name="com.android.systemui.permission.SELF" />
 
+    <permission android:name="com.android.systemui.permission.PLUGIN"
+            android:protectionLevel="signature" />
+
     <!-- Adding Quick Settings tiles -->
     <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
 
diff --git a/packages/SystemUI/plugin/Android.mk b/packages/SystemUI/plugin/Android.mk
new file mode 100644
index 0000000..86527db
--- /dev/null
+++ b/packages/SystemUI/plugin/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := SystemUIPluginLib
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAR_EXCLUDE_FILES := none
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core/res/res/color/watch_switch_track_color_material.xml b/packages/SystemUI/plugin/AndroidManifest.xml
similarity index 61%
copy from core/res/res/color/watch_switch_track_color_material.xml
copy to packages/SystemUI/plugin/AndroidManifest.xml
index 402a536..7c057dc 100644
--- a/core/res/res/color/watch_switch_track_color_material.xml
+++ b/packages/SystemUI/plugin/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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,9 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Used for the background of switch track for watch switch preference. -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="0.4" android:color="?attr/colorPrimary" />
-    <item android:color="?attr/colorPrimary" />
-</selector>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.systemui.plugins">
+
+    <uses-sdk
+        android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.mk b/packages/SystemUI/plugin/ExamplePlugin/Android.mk
new file mode 100644
index 0000000..4c82c75
--- /dev/null
+++ b/packages/SystemUI/plugin/ExamplePlugin/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PACKAGE_NAME := ExamplePlugin
+
+LOCAL_JAVA_LIBRARIES := SystemUIPluginLib
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml b/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml
new file mode 100644
index 0000000..bd2c71c
--- /dev/null
+++ b/packages/SystemUI/plugin/ExamplePlugin/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.systemui.plugin.testoverlayplugin">
+
+    <uses-permission android:name="com.android.systemui.permission.PLUGIN" />
+
+    <application>
+        <service android:name=".SampleOverlayPlugin">
+            <intent-filter>
+                <action android:name="com.android.systemui.action.PLUGIN_OVERLAY" />
+            </intent-filter>
+        </service>
+    </application>
+
+</manifest>
diff --git a/packages/SystemUI/plugin/ExamplePlugin/res/layout/colored_overlay.xml b/packages/SystemUI/plugin/ExamplePlugin/res/layout/colored_overlay.xml
new file mode 100644
index 0000000..b2910cb
--- /dev/null
+++ b/packages/SystemUI/plugin/ExamplePlugin/res/layout/colored_overlay.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** 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.
+-->
+
+<com.android.systemui.plugin.testoverlayplugin.CustomView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#80ff0000" />
diff --git a/packages/SystemUI/plugin/ExamplePlugin/src/com/android/systemui/plugin/testoverlayplugin/CustomView.java b/packages/SystemUI/plugin/ExamplePlugin/src/com/android/systemui/plugin/testoverlayplugin/CustomView.java
new file mode 100644
index 0000000..5fdbbf9
--- /dev/null
+++ b/packages/SystemUI/plugin/ExamplePlugin/src/com/android/systemui/plugin/testoverlayplugin/CustomView.java
@@ -0,0 +1,46 @@
+/*
+ * 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.plugin.testoverlayplugin;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * View with some logging to show that its being run.
+ */
+public class CustomView extends View {
+
+    private static final String TAG = "CustomView";
+
+    public CustomView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        Log.d(TAG, "new instance");
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        Log.d(TAG, "onAttachedToWindow");
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        Log.d(TAG, "onDetachedFromWindow");
+    }
+}
diff --git a/packages/SystemUI/plugin/ExamplePlugin/src/com/android/systemui/plugin/testoverlayplugin/SampleOverlayPlugin.java b/packages/SystemUI/plugin/ExamplePlugin/src/com/android/systemui/plugin/testoverlayplugin/SampleOverlayPlugin.java
new file mode 100644
index 0000000..a2f84dc
--- /dev/null
+++ b/packages/SystemUI/plugin/ExamplePlugin/src/com/android/systemui/plugin/testoverlayplugin/SampleOverlayPlugin.java
@@ -0,0 +1,71 @@
+/*
+ * 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.plugin.testoverlayplugin;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.OverlayPlugin;
+
+public class SampleOverlayPlugin implements OverlayPlugin {
+    private static final String TAG = "SampleOverlayPlugin";
+    private Context mPluginContext;
+
+    private View mStatusBarView;
+    private View mNavBarView;
+
+    @Override
+    public int getVersion() {
+        Log.d(TAG, "getVersion " + VERSION);
+        return VERSION;
+    }
+
+    @Override
+    public void onCreate(Context sysuiContext, Context pluginContext) {
+        Log.d(TAG, "onCreate");
+        mPluginContext = pluginContext;
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "onDestroy");
+        if (mStatusBarView != null) {
+            mStatusBarView.post(
+                    () -> ((ViewGroup) mStatusBarView.getParent()).removeView(mStatusBarView));
+        }
+        if (mNavBarView != null) {
+            mNavBarView.post(() -> ((ViewGroup) mNavBarView.getParent()).removeView(mNavBarView));
+        }
+    }
+
+    @Override
+    public void setup(View statusBar, View navBar) {
+        Log.d(TAG, "Setup");
+
+        if (statusBar instanceof ViewGroup) {
+            mStatusBarView = LayoutInflater.from(mPluginContext)
+                    .inflate(R.layout.colored_overlay, (ViewGroup) statusBar, false);
+            ((ViewGroup) statusBar).addView(mStatusBarView);
+        }
+        if (navBar instanceof ViewGroup) {
+            mNavBarView = LayoutInflater.from(mPluginContext)
+                    .inflate(R.layout.colored_overlay, (ViewGroup) navBar, false);
+            ((ViewGroup) navBar).addView(mNavBarView);
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/OverlayPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/OverlayPlugin.java
new file mode 100644
index 0000000..91a2604
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/OverlayPlugin.java
@@ -0,0 +1,24 @@
+/*
+ * 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.plugins;
+
+import android.view.View;
+
+public interface OverlayPlugin extends Plugin {
+
+    String ACTION = "com.android.systemui.action.PLUGIN_OVERLAY";
+    int VERSION = 1;
+
+    void setup(View statusBar, View navBar);
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java
new file mode 100644
index 0000000..b31b199
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/Plugin.java
@@ -0,0 +1,132 @@
+/*
+ * 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.plugins;
+
+import android.content.Context;
+
+/**
+ * Plugins are separate APKs that
+ * are expected to implement interfaces provided by SystemUI.  Their
+ * code is dynamically loaded into the SysUI process which can allow
+ * for multiple prototypes to be created and run on a single android
+ * build.
+ *
+ * PluginLifecycle:
+ * <pre class="prettyprint">
+ *
+ * plugin.onCreate(Context sysuiContext, Context pluginContext);
+ * --- This is always called before any other calls
+ *
+ * pluginListener.onPluginConnected(Plugin p);
+ * --- This lets the plugin hook know that a plugin is now connected.
+ *
+ * ** Any other calls back and forth between sysui/plugin **
+ *
+ * pluginListener.onPluginDisconnected(Plugin p);
+ * --- Lets the plugin hook know that it should stop interacting with
+ *     this plugin and drop all references to it.
+ *
+ * plugin.onDestroy();
+ * --- Finally the plugin can perform any cleanup to ensure that its not
+ *     leaking into the SysUI process.
+ *
+ * Any time a plugin APK is updated the plugin is destroyed and recreated
+ * to load the new code/resources.
+ *
+ * </pre>
+ *
+ * Creating plugin hooks:
+ *
+ * To create a plugin hook, first create an interface in
+ * frameworks/base/packages/SystemUI/plugin that extends Plugin.
+ * Include in it any hooks you want to be able to call into from
+ * sysui and create callback interfaces for anything you need to
+ * pass through into the plugin.
+ *
+ * Then to attach to any plugins simply add a plugin listener and
+ * onPluginConnected will get called whenever new plugins are installed,
+ * updated, or enabled.  Like this example from SystemUIApplication:
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * PluginManager.getInstance(this).addPluginListener(OverlayPlugin.COMPONENT,
+ *        new PluginListener<OverlayPlugin>() {
+ *        @Override
+ *        public void onPluginConnected(OverlayPlugin plugin) {
+ *            PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class);
+ *            if (phoneStatusBar != null) {
+ *                plugin.setup(phoneStatusBar.getStatusBarWindow(),
+ *                phoneStatusBar.getNavigationBarView());
+ *            }
+ *        }
+ * }, OverlayPlugin.VERSION, true /* Allow multiple plugins *\/);
+ * }
+ * </pre>
+ * Note the VERSION included here.  Any time incompatible changes in the
+ * interface are made, this version should be changed to ensure old plugins
+ * aren't accidentally loaded.  Since the plugin library is provided by
+ * SystemUI, default implementations can be added for new methods to avoid
+ * version changes when possible.
+ *
+ * Implementing a Plugin:
+ *
+ * See the ExamplePlugin for an example Android.mk on how to compile
+ * a plugin.  Note that SystemUILib is not static for plugins, its classes
+ * are provided by SystemUI.
+ *
+ * Plugin security is based around a signature permission, so plugins must
+ * hold the following permission in their manifest.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * <uses-permission android:name="com.android.systemui.permission.PLUGIN" />
+ * }
+ * </pre>
+ *
+ * A plugin is found through a querying for services, so to let SysUI know
+ * about it, create a service with a name that points at your implementation
+ * of the plugin interface with the action accompanying it:
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * <service android:name=".TestOverlayPlugin">
+ *    <intent-filter>
+ *        <action android:name="com.android.systemui.action.PLUGIN_COMPONENT" />
+ *    </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ */
+public interface Plugin {
+
+    /**
+     * Should be implemented as the following directly referencing the version constant
+     * from the plugin interface being implemented, this will allow recompiles to automatically
+     * pick up the current version.
+     * <pre class="prettyprint">
+     * {@literal
+     * public int getVersion() {
+     *     return VERSION;
+     * }
+     * }
+     * @return
+     */
+    int getVersion();
+
+    default void onCreate(Context sysuiContext, Context pluginContext) {
+    }
+
+    default void onDestroy() {
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
new file mode 100644
index 0000000..2a7139c
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -0,0 +1,342 @@
+/*
+ * 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.plugins;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.LayoutInflater;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import dalvik.system.PathClassLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PluginInstanceManager<T extends Plugin> extends BroadcastReceiver {
+
+    private static final boolean DEBUG = false;
+
+    private static final String TAG = "PluginInstanceManager";
+    private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
+
+    private final Context mContext;
+    private final PluginListener<T> mListener;
+    private final String mAction;
+    private final boolean mAllowMultiple;
+    private final int mVersion;
+
+    @VisibleForTesting
+    final MainHandler mMainHandler;
+    @VisibleForTesting
+    final PluginHandler mPluginHandler;
+    private final boolean isDebuggable;
+    private final PackageManager mPm;
+    private final ClassLoaderFactory mClassLoaderFactory;
+
+    PluginInstanceManager(Context context, String action, PluginListener<T> listener,
+            boolean allowMultiple, Looper looper, int version) {
+        this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
+                Build.IS_DEBUGGABLE, new ClassLoaderFactory());
+    }
+
+    @VisibleForTesting
+    PluginInstanceManager(Context context, PackageManager pm, String action,
+            PluginListener<T> listener, boolean allowMultiple, Looper looper, int version,
+            boolean debuggable, ClassLoaderFactory classLoaderFactory) {
+        mMainHandler = new MainHandler(Looper.getMainLooper());
+        mPluginHandler = new PluginHandler(looper);
+        mContext = context;
+        mPm = pm;
+        mAction = action;
+        mListener = listener;
+        mAllowMultiple = allowMultiple;
+        mVersion = version;
+        isDebuggable = debuggable;
+        mClassLoaderFactory = classLoaderFactory;
+    }
+
+    public void startListening() {
+        if (DEBUG) Log.d(TAG, "startListening");
+        mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(this, filter);
+        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiver(this, filter);
+    }
+
+    public void stopListening() {
+        if (DEBUG) Log.d(TAG, "stopListening");
+        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        for (PluginInfo plugin : plugins) {
+            mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
+                    plugin.mPlugin).sendToTarget();
+        }
+        mContext.unregisterReceiver(this);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (DEBUG) Log.d(TAG, "onReceive " + intent);
+        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+            mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
+        } else {
+            Uri data = intent.getData();
+            String pkgName = data.getEncodedSchemeSpecificPart();
+            mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkgName).sendToTarget();
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkgName).sendToTarget();
+            }
+        }
+    }
+
+    public boolean checkAndDisable(String className) {
+        boolean disableAny = false;
+        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        for (PluginInfo info : plugins) {
+            if (className.startsWith(info.mPackage)) {
+                disable(info);
+                disableAny = true;
+            }
+        }
+        return disableAny;
+    }
+
+    public void disableAll() {
+        ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+        plugins.forEach(this::disable);
+    }
+
+    private void disable(PluginInfo info) {
+        // Live by the sword, die by the sword.
+        // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
+
+        // If a plugin is detected in the stack of a crash then this will be called for that
+        // plugin, if the plugin causing a crash cannot be identified, they are all disabled
+        // assuming one of them must be bad.
+        Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
+        mPm.setComponentEnabledSetting(
+                new ComponentName(info.mPackage, info.mClass),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    private class MainHandler extends Handler {
+        private static final int PLUGIN_CONNECTED = 1;
+        private static final int PLUGIN_DISCONNECTED = 2;
+
+        public MainHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case PLUGIN_CONNECTED:
+                    if (DEBUG) Log.d(TAG, "onPluginConnected");
+                    PluginInfo<T> info = (PluginInfo<T>) msg.obj;
+                    info.mPlugin.onCreate(mContext, info.mPluginContext);
+                    mListener.onPluginConnected(info.mPlugin);
+                    break;
+                case PLUGIN_DISCONNECTED:
+                    if (DEBUG) Log.d(TAG, "onPluginDisconnected");
+                    mListener.onPluginDisconnected((T) msg.obj);
+                    ((T) msg.obj).onDestroy();
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
+            }
+        }
+    }
+
+    static class ClassLoaderFactory {
+        public ClassLoader createClassLoader(String path, ClassLoader base) {
+            return new PathClassLoader(path, base);
+        }
+    }
+
+    private class PluginHandler extends Handler {
+        private static final int QUERY_ALL = 1;
+        private static final int QUERY_PKG = 2;
+        private static final int REMOVE_PKG = 3;
+
+        private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
+
+        public PluginHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case QUERY_ALL:
+                    if (DEBUG) Log.d(TAG, "queryAll " + mAction);
+                    for (int i = mPlugins.size() - 1; i >= 0; i--) {
+                        PluginInfo<T> plugin = mPlugins.get(i);
+                        mListener.onPluginDisconnected(plugin.mPlugin);
+                        plugin.mPlugin.onDestroy();
+                    }
+                    mPlugins.clear();
+                    handleQueryPlugins(null);
+                    break;
+                case REMOVE_PKG:
+                    String pkg = (String) msg.obj;
+                    for (int i = mPlugins.size() - 1; i >= 0; i--) {
+                        final PluginInfo<T> plugin = mPlugins.get(i);
+                        if (plugin.mPackage.equals(pkg)) {
+                            mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
+                                    plugin.mPlugin).sendToTarget();
+                            mPlugins.remove(i);
+                        }
+                    }
+                    break;
+                case QUERY_PKG:
+                    String p = (String) msg.obj;
+                    if (DEBUG) Log.d(TAG, "queryPkg " + p + " " + mAction);
+                    if (mAllowMultiple || (mPlugins.size() == 0)) {
+                        handleQueryPlugins(p);
+                    } else {
+                        if (DEBUG) Log.d(TAG, "Too many of " + mAction);
+                    }
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+
+        private void handleQueryPlugins(String pkgName) {
+            // This isn't actually a service and shouldn't ever be started, but is
+            // a convenient PM based way to manage our plugins.
+            Intent intent = new Intent(mAction);
+            if (pkgName != null) {
+                intent.setPackage(pkgName);
+            }
+            List<ResolveInfo> result =
+                    mPm.queryIntentServices(intent, 0);
+            if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
+            if (result.size() > 1 && !mAllowMultiple) {
+                // TODO: Show warning.
+                Log.w(TAG, "Multiple plugins found for " + mAction);
+                return;
+            }
+            for (ResolveInfo info : result) {
+                ComponentName name = new ComponentName(info.serviceInfo.packageName,
+                        info.serviceInfo.name);
+                PluginInfo<T> t = handleLoadPlugin(name);
+                if (t == null) continue;
+                mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
+                mPlugins.add(t);
+            }
+        }
+
+        protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
+            // This was already checked, but do it again here to make extra extra sure, we don't
+            // use these on production builds.
+            if (!isDebuggable) {
+                // Never ever ever allow these on production builds, they are only for prototyping.
+                Log.d(TAG, "Somehow hit second debuggable check");
+                return null;
+            }
+            String pkg = component.getPackageName();
+            String cls = component.getClassName();
+            try {
+                PackageManager pm = mPm;
+                ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
+                // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
+                if (pm.checkPermission(PLUGIN_PERMISSION, pkg)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    Log.d(TAG, "Plugin doesn't have permission: " + pkg);
+                    return null;
+                }
+                // Create our own ClassLoader so we can use our own code as the parent.
+                ClassLoader classLoader = mClassLoaderFactory.createClassLoader(info.sourceDir,
+                        getClass().getClassLoader());
+                Context pluginContext = new PluginContextWrapper(
+                        mContext.createApplicationContext(info, 0), classLoader);
+                Class<?> pluginClass = Class.forName(cls, true, classLoader);
+                T plugin = (T) pluginClass.newInstance();
+                if (plugin.getVersion() != mVersion) {
+                    // TODO: Warn user.
+                    Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
+                            + ", expected " + mVersion);
+                    return null;
+                }
+                if (DEBUG) Log.d(TAG, "createPlugin");
+                return new PluginInfo(pkg, cls, plugin, pluginContext);
+            } catch (Exception e) {
+                Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+                return null;
+            }
+        }
+    }
+
+    public static class PluginContextWrapper extends ContextWrapper {
+        private final ClassLoader mClassLoader;
+        private LayoutInflater mInflater;
+
+        public PluginContextWrapper(Context base, ClassLoader classLoader) {
+            super(base);
+            mClassLoader = classLoader;
+        }
+
+        @Override
+        public ClassLoader getClassLoader() {
+            return mClassLoader;
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+                if (mInflater == null) {
+                    mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
+                }
+                return mInflater;
+            }
+            return getBaseContext().getSystemService(name);
+        }
+    }
+
+    private static class PluginInfo<T> {
+        private final Context mPluginContext;
+        private T mPlugin;
+        private String mClass;
+        private String mPackage;
+
+        public PluginInfo(String pkg, String cls, T plugin, Context pluginContext) {
+            mPlugin = plugin;
+            mClass = cls;
+            mPackage = pkg;
+            mPluginContext = pluginContext;
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java
new file mode 100644
index 0000000..b2f92d6
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java
@@ -0,0 +1,36 @@
+/*
+ * 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.plugins;
+
+/**
+ * Interface for listening to plugins being connected.
+ */
+public interface PluginListener<T extends Plugin> {
+    /**
+     * Called when the plugin has been loaded and is ready to be used.
+     * This may be called multiple times if multiple plugins are allowed.
+     * It may also be called in the future if the plugin package changes
+     * and needs to be reloaded.
+     */
+    void onPluginConnected(T plugin);
+
+    /**
+     * Called when a plugin has been uninstalled/updated and should be removed
+     * from use.
+     */
+    default void onPluginDisconnected(T plugin) {
+        // Optional.
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
new file mode 100644
index 0000000..aa0b3c5
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
@@ -0,0 +1,138 @@
+/*
+ * 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.plugins;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * @see Plugin
+ */
+public class PluginManager {
+
+    private static PluginManager sInstance;
+
+    private final HandlerThread mBackgroundThread;
+    private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+            = new ArrayMap<>();
+    private final Context mContext;
+    private final PluginInstanceManagerFactory mFactory;
+    private final boolean isDebuggable;
+
+    private PluginManager(Context context) {
+        this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
+                Thread.getDefaultUncaughtExceptionHandler());
+    }
+
+    @VisibleForTesting
+    PluginManager(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
+            UncaughtExceptionHandler defaultHandler) {
+        mContext = context;
+        mFactory = factory;
+        mBackgroundThread = new HandlerThread("Plugins");
+        mBackgroundThread.start();
+        isDebuggable = debuggable;
+
+        PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
+                defaultHandler);
+        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+    }
+
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            int version) {
+        addPluginListener(action, listener, version, false);
+    }
+
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            int version, boolean allowMultiple) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return;
+        }
+        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
+                allowMultiple, mBackgroundThread.getLooper(), version);
+        p.startListening();
+        mPluginMap.put(listener, p);
+    }
+
+    public void removePluginListener(PluginListener<?> listener) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return;
+        }
+        if (!mPluginMap.containsKey(listener)) return;
+        mPluginMap.remove(listener).stopListening();
+    }
+
+    public static PluginManager getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new PluginManager(context.getApplicationContext());
+        }
+        return sInstance;
+    }
+
+    @VisibleForTesting
+    public static class PluginInstanceManagerFactory {
+        public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
+                String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
+                int version) {
+            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
+                    version);
+        }
+    }
+
+    private class PluginExceptionHandler implements UncaughtExceptionHandler {
+        private final UncaughtExceptionHandler mHandler;
+
+        private PluginExceptionHandler(UncaughtExceptionHandler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void uncaughtException(Thread thread, Throwable throwable) {
+            // Search for and disable plugins that may have been involved in this crash.
+            boolean disabledAny = checkStack(throwable);
+            if (!disabledAny) {
+                // We couldn't find any plugins involved in this crash, just to be safe
+                // disable all the plugins, so we can be sure that SysUI is running as
+                // best as possible.
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.disableAll();
+                }
+            }
+
+            // Run the normal exception handler so we can crash and cleanup our state.
+            mHandler.uncaughtException(thread, throwable);
+        }
+
+        private boolean checkStack(Throwable throwable) {
+            if (throwable == null) return false;
+            boolean disabledAny = false;
+            for (StackTraceElement element : throwable.getStackTrace()) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    disabledAny |= manager.checkAndDisable(element.getClassName());
+                }
+            }
+            return disabledAny | checkStack(throwable.getCause());
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginUtils.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginUtils.java
new file mode 100644
index 0000000..af49d43
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginUtils.java
@@ -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.
+ */
+
+package com.android.systemui.plugins;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+public class PluginUtils {
+
+    public static void setId(Context sysuiContext, View view, String id) {
+        int i = sysuiContext.getResources().getIdentifier(id, "id", sysuiContext.getPackageName());
+        view.setId(i);
+    }
+}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 9182f7e..364885a 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -37,3 +37,6 @@
 
 -keep class ** extends android.support.v14.preference.PreferenceFragment
 -keep class com.android.systemui.tuner.*
+-keep class com.android.systemui.plugins.** {
+    public protected **;
+}
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml b/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
index f63dfb12..b78d3bf 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,17 +15,15 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="32dp"
-        android:height="32dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
-    <path
-        android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
-        android:fillColor="#4DFFFFFF"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
index 7fb423e..e055de7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,20 +15,18 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="32dp"
-        android:height="32dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.7,20.0l2.0,0.0l0.0,2.0l-2.0,0.0z"/>
+        android:pathData="M10.0,14.6l-8.0,8.0l8.0,0.0l0,-8z"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.7,10.0l2.0,0.0l0.0,8.1l-2.0,0.0z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M17.7,8.0l4.299999,0.0 0.0,-6.0 -20.0,20.0 15.700001,0.0z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_2.xml b/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
index 3358d65..8a48817 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,20 +15,18 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="32dp"
-        android:height="32dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
+        android:pathData="M14.0,10.6l-12.0,12.0l12.0,0.0L14.0,10.6z"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M13.900000,10.000000l-11.900000,12.000000 11.900000,0.000000z"/>
-    <path
-        android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
-        android:fillColor="#4DFFFFFF"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
index 63838a9..39cc94c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,20 +15,18 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="32dp"
-        android:height="32dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,19.900000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,9.900000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M16.700001,7.200000l-14.700001,14.700000 14.700001,0.000000z"/>
-    <path
-        android:pathData="M17.700001,7.900000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
-        android:fillColor="#4DFFFFFF"/>
+        android:pathData="M14.1,14.1l2.9,0.0 0.0,-6.5 -15.0,15.0 12.1,0.0z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
index 76690cc..012e95e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,17 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="32dp"
-        android:height="32dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
+        android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M2.000000,22.000000l15.700001,0.000000 0.000000,-14.000000 4.299999,0.000000 0.000000,-6.000000z"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
index 50c427e..e6f9292 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -14,17 +14,15 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="29.5dp"
-        android:viewportWidth="26.0"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.200000,-1.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.799999,-1.800001z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
index a2d11a0..d423ccb 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -14,20 +14,18 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="29.5dp"
-        android:viewportWidth="26.0"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M13.000000,22.000000l5.500000,-6.800000c-0.200000,-0.200000 -2.300000,-1.900000 -5.500000,-1.900000s-5.300000,1.800000 -5.500000,1.900000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.799999,-1.800001z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
index f2043fc..1982130 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -14,20 +14,18 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="29.5dp"
-        android:viewportWidth="26.0"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.000000,11.600000c-1.300000,-0.700000 -3.400000,-1.600000 -6.000000,-1.600000c-4.400000,0.000000 -7.300000,2.400000 -7.600000,2.700000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,11.600000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.800001,1.9 -1.800001,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.9,-1.800001z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
index b7a4f4c..b350111 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -14,20 +14,18 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="29.5dp"
-        android:viewportWidth="26.0"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.000000,8.600000c-1.600000,-0.700000 -3.600000,-1.300000 -6.000000,-1.300000c-5.300000,0.000000 -8.900000,3.000000 -9.200000,3.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.600000z"/>
+        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"
+        android:fillColor="#FFFFFF"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.9,-1.800001z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
index 35a9138..136a004 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -14,17 +14,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="29.5dp"
-        android:viewportWidth="26.0"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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"/>
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.9,-1.800001z"
+        android:fillColor="#FFFFFF"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_0.xml b/packages/SystemUI/res/drawable/stat_sys_signal_0.xml
index 643c4f9..8bc872a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_0.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_0.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,17 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
+        android:width="17.0dp"
+        android:height="17.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
-    <path
-        android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
+        android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillColor="?attr/backgroundColor"/>
+    <path
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_1.xml b/packages/SystemUI/res/drawable/stat_sys_signal_1.xml
index 64781c3..8fa7630 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_1.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_1.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,20 +15,17 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
+        android:width="17.0dp"
+        android:height="17.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.7,20.0l2.0,0.0l0.0,2.0l-2.0,0.0z"/>
+        android:pathData="M10.0,14.6l-8.0,8.0l8.0,0.0l0,-8z"
+        android:fillColor="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.7,10.0l2.0,0.0l0.0,8.1l-2.0,0.0z"/>
+        android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
+        android:fillColor="?attr/backgroundColor"/>
     <path
-        android:fillColor="?attr/backgroundColor"
-        android:pathData="M17.7,8.0l4.299999,0.0 0.0,-6.0 -20.0,20.0 15.700001,0.0z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_2.xml b/packages/SystemUI/res/drawable/stat_sys_signal_2.xml
index eb2be08..2a660a3 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_2.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_2.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,20 +15,17 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
+        android:width="17.0dp"
+        android:height="17.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
+        android:pathData="M14.0,10.6l-12.0,12.0l12.0,0.0L14.0,10.6z"
+        android:fillColor="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M13.900000,10.000000l-11.900000,12.000000 11.900000,0.000000z"/>
-    <path
-        android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
+        android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillColor="?attr/backgroundColor"/>
+    <path
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_3.xml b/packages/SystemUI/res/drawable/stat_sys_signal_3.xml
index 22afad0..9e0a433 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_3.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_3.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,20 +15,17 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
+        android:width="17.0dp"
+        android:height="17.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,19.900000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,9.900000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M16.700001,7.200000l-14.700001,14.700000 14.700001,0.000000z"/>
-    <path
-        android:pathData="M17.700001,7.900000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
+        android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillColor="?attr/backgroundColor"/>
+    <path
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="?attr/fillColor"/>
+    <path
+        android:pathData="M14.1,14.1l2.9,0.0 0.0,-6.5 -15.0,15.0 12.1,0.0z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_4.xml b/packages/SystemUI/res/drawable/stat_sys_signal_4.xml
index d1e866d..01f6703 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_4.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_4.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,18 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:autoMirrored="true"
-        android:width="17dp"
-        android:height="17dp"
+        android:width="17.0dp"
+        android:height="17.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
-
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
+        android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
+        android:fillColor="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M2.000000,22.000000l15.700001,0.000000 0.000000,-14.000000 4.299999,0.000000 0.000000,-6.000000z"/>
+        android:pathData="M21.9,17.0l-1.1,-1.1 -1.9,1.9 -1.9,-1.9 -1.1,1.1 1.9,1.9 -1.9,1.9 1.1,1.1 1.9,-1.9 1.9,1.9 1.1,-1.1 -1.9,-1.9z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml
index 7f1b715e..2de2e36 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,16 +15,13 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="18.41dp"
-        android:height="17dp"
-        android:viewportWidth="26.0"
+        android:height="18.41dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/backgroundColor"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.200000,-1.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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="?attr/backgroundColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.799999,-1.800001z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml
index acd89be..144a7c1 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,19 +15,16 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="18.41dp"
-        android:height="17dp"
-        android:viewportWidth="26.0"
+        android:height="18.41dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/backgroundColor"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M13.000000,22.000000l5.500000,-6.800000c-0.200000,-0.200000 -2.300000,-1.900000 -5.500000,-1.900000s-5.300000,1.800000 -5.500000,1.900000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
+        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="?attr/backgroundColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.799999,-1.800001z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml
index f33b25c..6b7f712 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,19 +15,16 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="18.41dp"
-        android:height="17dp"
-        android:viewportWidth="26.0"
+        android:height="18.41dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/backgroundColor"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M19.000000,11.600000c-1.300000,-0.700000 -3.400000,-1.600000 -6.000000,-1.600000c-4.400000,0.000000 -7.300000,2.400000 -7.600000,2.700000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,11.600000z"/>
+        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="?attr/backgroundColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.800001,1.9 -1.800001,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.9,-1.800001z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml
index 09d2e50..d34b4de 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,19 +15,16 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="18.41dp"
-        android:height="17dp"
-        android:viewportWidth="26.0"
+        android:height="18.41dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/backgroundColor"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M19.000000,8.600000c-1.600000,-0.700000 -3.600000,-1.300000 -6.000000,-1.300000c-5.300000,0.000000 -8.900000,3.000000 -9.200000,3.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.600000z"/>
+        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="?attr/backgroundColor"/>
     <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/fillColor"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.9,-1.800001z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml
index fb1f584..5701356 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml
@@ -1,7 +1,7 @@
 <!--
-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");
+    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
 
@@ -15,16 +15,13 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="18.41dp"
-        android:height="17dp"
-        android:viewportWidth="26.0"
+        android:height="18.41dp"
+        android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
+        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="?attr/fillColor"/>
     <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
-    <path
-        android:fillColor="?attr/singleToneColor"
-        android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
+        android:pathData="M21.9,15.4l-1.1,-1.2 -1.9,1.900001 -1.9,-1.900001 -1.1,1.2 1.9,1.9 -1.9,1.800001 1.1,1.199999 1.9,-1.9 1.9,1.9 1.1,-1.199999 -1.9,-1.800001z"
+        android:fillColor="?attr/fillColor"/>
 </vector>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 6fc1bdd..8aef790 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -346,8 +346,8 @@
     <string name="description_direction_left" msgid="7207478719805562165">"لغزاندن به چپ برای <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="zen_priority_introduction" msgid="3070506961866919502">"صداها و لرزش‌هایی به جز هشدارها، یادآوری‌ها، رویدادها و تماس‌گیرنده‌هایی که مشخص می‌کنید، مزاحم شما نمی‌شوند."</string>
     <string name="zen_priority_customize_button" msgid="7948043278226955063">"سفارشی کردن"</string>
-    <string name="zen_silence_introduction_voice" msgid="2284540992298200729">"این کار «همه» صداها و لرزش‌ها از جمله هشدارها، موسیقی، ویدیوها و بازی‌ها را مسدود می‌کند. همچنان می‌توانید تماس تلفنی برقرار کنید."</string>
-    <string name="zen_silence_introduction" msgid="3137882381093271568">"این کار «همه» صداها و لرزش‌ها از جمله هشدارها، موسیقی، ویدیوها و بازی‌ها را مسدود می‌کند."</string>
+    <string name="zen_silence_introduction_voice" msgid="2284540992298200729">"این کار «همه» صداها و لرزش‌ها از جمله هشدارها، موسیقی، ویدئوها و بازی‌ها را مسدود می‌کند. همچنان می‌توانید تماس تلفنی برقرار کنید."</string>
+    <string name="zen_silence_introduction" msgid="3137882381093271568">"این کار «همه» صداها و لرزش‌ها از جمله هشدارها، موسیقی، ویدئوها و بازی‌ها را مسدود می‌کند."</string>
     <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
     <string name="speed_bump_explanation" msgid="1288875699658819755">"اعلان‌های کمتر فوری در زیر"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"دوباره ضربه بزنید تا باز شود"</string>
diff --git a/packages/SystemUI/res/values-fa/strings_tv.xml b/packages/SystemUI/res/values-fa/strings_tv.xml
index 2894abba..b97a6465 100644
--- a/packages/SystemUI/res/values-fa/strings_tv.xml
+++ b/packages/SystemUI/res/values-fa/strings_tv.xml
@@ -25,7 +25,7 @@
     <string name="pip_pause" msgid="8412075640017218862">"مکث"</string>
     <string name="pip_hold_home" msgid="340086535668778109">"‏کنترل PIP ‏با نگه‌داشتن "<b>"HOME"</b></string>
     <string name="pip_onboarding_title" msgid="7850436557670253991">"تصویر در تصویر"</string>
-    <string name="pip_onboarding_description" msgid="4028124563309465267">"تا زمانی که ویدیوی دیگری را پخش کنید، این صفحه حالت ویدیو در ویدیوی شما را حفظ می‌کند. برای کنترل آن، دکمه "<b>"صفحه اصلی"</b>" را فشار دهید و نگه دارید."</string>
+    <string name="pip_onboarding_description" msgid="4028124563309465267">"تا زمانی که ویدئوی دیگری را پخش کنید، این صفحه حالت ویدئو در ویدئوی شما را حفظ می‌کند. برای کنترل آن، دکمه "<b>"صفحه اصلی"</b>" را فشار دهید و نگه دارید."</string>
     <string name="pip_onboarding_button" msgid="3957426748484904611">"متوجه شدم"</string>
     <string name="recents_tv_dismiss" msgid="3555093879593377731">"رد کردن"</string>
   <string-array name="recents_tv_blacklist_array">
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index fd6cc4e..c781403 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -403,7 +403,7 @@
     <string name="device_owned_footer" msgid="3802752663326030053">"डिवाइस को मॉनीटर किया जा सकता है"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"प्रोफ़ाइल को मॉनीटर किया जा सकता है"</string>
     <string name="vpn_footer" msgid="2388611096129106812">"नेटवर्क को मॉनीटर किया जा सकता है"</string>
-    <string name="branded_vpn_footer" msgid="2168111859226496230">"नेटवर्क को मॉनीटर किया जा सकता है"</string>
+    <string name="branded_vpn_footer" msgid="2168111859226496230">"नेटवर्क को मॉनिटर किया जा सकता है"</string>
     <string name="monitoring_title_device_owned" msgid="7121079311903859610">"डिवाइस को मॉनीटर करना"</string>
     <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"प्रोफ़ाइल को मॉनीटर करना"</string>
     <string name="monitoring_title" msgid="169206259253048106">"नेटवर्क को मॉनीटर करना"</string>
@@ -416,7 +416,7 @@
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
-    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइट सहित आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि को मॉनीटर कर सकता है."</string>
+    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइट सहित आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि को मॉनिटर कर सकता है."</string>
     <string name="monitoring_description_app_work" msgid="1754325860918060897">"आपकी कार्य प्रोफ़ाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> के द्वारा प्रबंधित है. वह <xliff:g id="APPLICATION">%2$s</xliff:g> से कनेक्‍ट है, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी कार्य नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nअधिक जानकारी के लिए, अपने नियंत्रक से संपर्क करें."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"आपकी कार्य प्रोफ़ाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> के द्वारा प्रबंधित है. वह <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> से कनेक्‍ट है, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी कार्य नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nआप <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> से भी कनेक्‍ट हैं, जो आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
     <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"आपका डिवाइस <xliff:g id="ORGANIZATION">%1$s</xliff:g> के द्वारा प्रबंधित है.\n\nआपका नियंत्रक सेटिंग, कॉर्पोरेट ऐक्‍सेस, ऐप्स, आपके डिवाइस से संबद्ध डेटा और आपके डिवाइस की स्‍थान जानकारी की निगरानी और उसका प्रबंधन कर सकता है.\n\nआप <xliff:g id="APPLICATION">%2$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nअधिक जानकारी के लिए, अपने नियंत्रक से संपर्क करें."</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 0d414f4..e5bc8b9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -217,7 +217,7 @@
     <style name="Animation.StatusBar">
     </style>
 
-    <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" />
+    <style name="systemui_theme" parent="@*android:style/Theme.DeviceDefault.Settings.Dark" />
 
     <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:colorAccent">@color/remote_input_accent</item>
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTracker.java b/packages/SystemUI/src/com/android/systemui/LatencyTracker.java
new file mode 100644
index 0000000..cc25ee3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTracker.java
@@ -0,0 +1,123 @@
+/*
+ * 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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.util.Log;
+import android.util.SparseLongArray;
+
+/**
+ * Class to track various latencies in SystemUI. It then outputs the latency to logcat so these
+ * latencies can be captured by tests and then used for dashboards.
+ */
+public class LatencyTracker {
+
+    private static final String ACTION_RELOAD_PROPERTY =
+            "com.android.systemui.RELOAD_LATENCY_TRACKER_PROPERTY";
+
+    private static final String TAG = "LatencyTracker";
+
+    /**
+     * Time it takes until the first frame of the notification panel to be displayed while expanding
+     */
+    public static final int ACTION_EXPAND_PANEL = 0;
+
+    /**
+     * Time it takes until the first frame of recents is drawn after invoking it with the button.
+     */
+    public static final int ACTION_TOGGLE_RECENTS = 1;
+
+    /**
+     * Time between we get a fingerprint acquired signal until we start with the unlock animation
+     */
+    public static final int ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 2;
+
+    private static final String[] NAMES = new String[] {
+            "expand panel",
+            "toggle recents",
+            "fingerprint wake-and-unlock" };
+
+    private static LatencyTracker sLatencyTracker;
+
+    private final SparseLongArray mStartRtc = new SparseLongArray();
+    private boolean mEnabled;
+
+    public static LatencyTracker getInstance(Context context) {
+        if (sLatencyTracker == null) {
+            sLatencyTracker = new LatencyTracker(context);
+        }
+        return sLatencyTracker;
+    }
+
+    private LatencyTracker(Context context) {
+        context.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                reloadProperty();
+            }
+        }, new IntentFilter(ACTION_RELOAD_PROPERTY));
+        reloadProperty();
+    }
+
+    private void reloadProperty() {
+        mEnabled = SystemProperties.getBoolean("debug.systemui.latency_tracking", false);
+    }
+
+    public static boolean isEnabled(Context ctx) {
+        return Build.IS_DEBUGGABLE && getInstance(ctx).mEnabled;
+    }
+
+    /**
+     * Notifies that an action is starting. This needs to be called from the main thread.
+     *
+     * @param action The action to start. One of the ACTION_* values.
+     */
+    public void onActionStart(int action) {
+        if (!mEnabled) {
+            return;
+        }
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], 0);
+        mStartRtc.put(action, SystemClock.elapsedRealtime());
+    }
+
+    /**
+     * Notifies that an action has ended. This needs to be called from the main thread.
+     *
+     * @param action The action to end. One of the ACTION_* values.
+     */
+    public void onActionEnd(int action) {
+        if (!mEnabled) {
+            return;
+        }
+        long endRtc = SystemClock.elapsedRealtime();
+        long startRtc = mStartRtc.get(action, -1);
+        if (startRtc == -1) {
+            return;
+        }
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0);
+        long duration = endRtc - startRtc;
+        Log.i(TAG, "action=" + action + " latency=" + duration);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 52b5a54..e300aff 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,18 +16,24 @@
 
 package com.android.systemui;
 
+import android.app.ActivityThread;
 import android.app.Application;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.Log;
 
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -106,6 +112,13 @@
                 }
             }, filter);
         } else {
+            // We don't need to startServices for sub-process that is doing some tasks.
+            // (screenshots, sweetsweetdesserts or tuner ..)
+            String processName = ActivityThread.currentProcessName();
+            ApplicationInfo info = getApplicationInfo();
+            if (processName != null && processName.startsWith(info.processName + ":")) {
+                return;
+            }
             // For a secondary user, boot-completed will never be called because it has already
             // been broadcasted on startup for the primary SystemUI process.  Instead, for
             // components which require the SystemUI component to be initialized per-user, we
@@ -174,6 +187,18 @@
                 mServices[i].onBootCompleted();
             }
         }
+        PluginManager.getInstance(this).addPluginListener(OverlayPlugin.ACTION,
+                new PluginListener<OverlayPlugin>() {
+            @Override
+            public void onPluginConnected(OverlayPlugin plugin) {
+                PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class);
+                if (phoneStatusBar != null) {
+                    plugin.setup(phoneStatusBar.getStatusBarWindow(),
+                            phoneStatusBar.getNavigationBarView());
+                }
+            }
+        }, OverlayPlugin.VERSION, true /* Allow multiple plugins */);
+
         mServicesStarted = true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index aa6e88c..b9e8acb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -976,6 +976,7 @@
      * if there is a secure lock pattern.
      */
     public void onDreamingStarted() {
+        KeyguardUpdateMonitor.getInstance(mContext).dispatchDreamingStarted();
         synchronized (this) {
             if (mDeviceInteractive
                     && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
@@ -988,6 +989,7 @@
      * A dream stopped.
      */
     public void onDreamingStopped() {
+        KeyguardUpdateMonitor.getInstance(mContext).dispatchDreamingStopped();
         synchronized (this) {
             if (mDeviceInteractive) {
                 cancelDoKeyguardLaterLocked();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index b831235..8a500c3 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -31,6 +31,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -73,7 +74,7 @@
     private final Context mContext;
     private final NotificationManager mNoMan;
     private final PowerManager mPowerMan;
-    private final Handler mHandler = new Handler();
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Receiver mReceiver = new Receiver();
     private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
 
@@ -89,9 +90,10 @@
     private boolean mInvalidCharger;
     private SystemUIDialog mSaverConfirmation;
 
-    public PowerNotificationWarnings(Context context, PhoneStatusBar phoneStatusBar) {
+    public PowerNotificationWarnings(Context context, NotificationManager notificationManager,
+            PhoneStatusBar phoneStatusBar) {
         mContext = context;
-        mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        mNoMan = notificationManager;
         mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mReceiver.init();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 109a456..b651f2d9 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.power;
 
+import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -30,10 +31,8 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
-
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -60,7 +59,10 @@
     public void start() {
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
-        mWarnings = new PowerNotificationWarnings(mContext, getComponent(PhoneStatusBar.class));
+        mWarnings = new PowerNotificationWarnings(
+                mContext,
+                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE),
+                getComponent(PhoneStatusBar.class));
 
         ContentObserver obs = new ContentObserver(mHandler) {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index c432096..867f3b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -63,7 +63,7 @@
 
     // Bind retry control.
     private static final int MAX_BIND_RETRIES = 5;
-    private static final int BIND_RETRY_DELAY = 1000;
+    private static final int DEFAULT_BIND_RETRY_DELAY = 1000;
 
     // Shared prefs that hold tile lifecycle info.
     private static final String TILES = "tiles_prefs";
@@ -81,8 +81,8 @@
     private IBinder mClickBinder;
 
     private int mBindTryCount;
+    private int mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
     private boolean mBound;
-    @VisibleForTesting
     boolean mReceiverRegistered;
     private boolean mUnbindImmediate;
     private TileChangeListener mChangeListener;
@@ -117,6 +117,10 @@
         }
     }
 
+    public void setBindRetryDelay(int delayMs) {
+        mBindRetryDelay = delayMs;
+    }
+
     public boolean isActiveTile() {
         try {
             ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
@@ -264,7 +268,7 @@
                         setBindService(true);
                     }
                 }
-            }, BIND_RETRY_DELAY);
+            }, mBindRetryDelay);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 7bdb1c4..784d2ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -40,7 +40,9 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
+import com.android.systemui.LatencyTracker;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
@@ -187,6 +189,11 @@
                 public boolean onPreDraw() {
                     mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
                     EventBus.getDefault().post(new RecentsDrawnEvent());
+                    if (LatencyTracker.isEnabled(getApplicationContext())) {
+                        DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(
+                                getApplicationContext()).onActionEnd(
+                                LatencyTracker.ACTION_TOGGLE_RECENTS));
+                    }
                     return true;
                 }
             };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 75b9417..a7132e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -113,8 +113,11 @@
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
 
@@ -268,6 +271,8 @@
 
     protected boolean mVrMode;
 
+    private Set<String> mNonBlockablePkgs;
+
     @Override  // NotificationData.Environment
     public boolean isDeviceProvisioned() {
         return mDeviceProvisioned;
@@ -829,6 +834,9 @@
             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
         }
 
+        mNonBlockablePkgs = new HashSet<String>();
+        Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_nonBlockableNotificationPackages));
     }
 
     protected void notifyUserAboutHiddenNotifications() {
@@ -1109,7 +1117,8 @@
             settingsButton.setVisibility(View.GONE);
         }
 
-        guts.bindImportance(pmUser, sbn, mNotificationData.getImportance(sbn.getKey()));
+        guts.bindImportance(pmUser, sbn, mNonBlockablePkgs,
+                mNotificationData.getImportance(sbn.getKey()));
 
         final TextView doneButton = (TextView) guts.findViewById(R.id.done);
         doneButton.setText(R.string.notification_done);
@@ -1949,9 +1958,18 @@
                                             .getIdentifier();
                                     if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
                                             && mKeyguardManager.isDeviceLocked(userId)) {
-                                        if (startWorkChallengeIfNecessary(userId,
-                                                intent.getIntentSender(), notificationKey)) {
-                                            // Show work challenge, do not run pendingintent and
+                                        boolean canBypass = false;
+                                        try {
+                                            canBypass = ActivityManagerNative.getDefault()
+                                                    .canBypassWorkChallenge(intent);
+                                        } catch (RemoteException e) {
+                                        }
+                                        // For direct-boot aware activities, they can be shown when
+                                        // the device is still locked without triggering the work
+                                        // challenge.
+                                        if ((!canBypass) && startWorkChallengeIfNecessary(userId,
+                                                    intent.getIntentSender(), notificationKey)) {
+                                            // Show work challenge, do not run PendingIntent and
                                             // remove notification
                                             return;
                                         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
index 2045ec8..1d7bede 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
@@ -56,6 +56,8 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         mDismissButton.setText(R.string.clear_all_notifications_text);
+        mDismissButton.setContentDescription(
+                mContext.getString(R.string.accessibility_clear_all));
     }
 
     public boolean isButtonVisible() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 68de16b..caf5447 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -905,6 +905,9 @@
     }
 
     public void resetTranslation() {
+        if (mTranslateAnim != null) {
+            mTranslateAnim.cancel();
+        }
         if (mTranslateableViews != null) {
             for (int i = 0; i < mTranslateableViews.size(); i++) {
                 mTranslateableViews.get(i).setTranslationX(0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 88f37a3..8b4225a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -174,7 +174,10 @@
 
     private void drawBackgroundCircle(Canvas canvas) {
         if (mCircleRadius > 0 || mFinishing) {
-            if (mFinishing && mSupportHardware) {
+            if (mFinishing && mSupportHardware && mHwCenterX != null) {
+                // Our hardware drawing proparties can be null if the finishing started but we have
+                // never drawn before. In that case we are not doing a render thread animation
+                // anyway, so we need to use the normal drawing.
                 DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
                 displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
                         mHwCirclePaint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 9fd09d9..78e56c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -205,7 +205,7 @@
                     && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
                 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
                         width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
-                        MeasureSpec.AT_MOST);
+                        MeasureSpec.EXACTLY);
             }
             mSingleLineView.measure(singleLineWidthSpec,
                     MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index c497cfd..62d730a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -50,6 +50,8 @@
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.tuner.TunerService;
 
+import java.util.Set;
+
 /**
  * The guts of a notification revealed when performing a long press.
  */
@@ -173,7 +175,7 @@
     }
 
     void bindImportance(final PackageManager pm, final StatusBarNotification sbn,
-            final int importance) {
+            final Set<String> nonBlockablePkgs, final int importance) {
         mINotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
         mStartingUserImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
@@ -182,24 +184,26 @@
                     mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
         } catch (RemoteException e) {}
         mNotificationImportance = importance;
-        boolean systemApp = false;
+        boolean nonBlockable = false;
         try {
             final PackageInfo info =
                     pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES);
-            systemApp = Utils.isSystemPackage(getResources(), pm, info);
+            nonBlockable = Utils.isSystemPackage(getResources(), pm, info);
         } catch (PackageManager.NameNotFoundException e) {
             // unlikely.
         }
+        if (nonBlockablePkgs != null) {
+            nonBlockable |= nonBlockablePkgs.contains(sbn.getPackageName());
+        }
 
         final View importanceSlider = findViewById(R.id.importance_slider);
         final View importanceButtons = findViewById(R.id.importance_buttons);
         if (mShowSlider) {
-            bindSlider(importanceSlider, systemApp);
+            bindSlider(importanceSlider, nonBlockable);
             importanceSlider.setVisibility(View.VISIBLE);
             importanceButtons.setVisibility(View.GONE);
         } else {
-
-            bindToggles(importanceButtons, mStartingUserImportance, systemApp);
+            bindToggles(importanceButtons, mStartingUserImportance, nonBlockable);
             importanceButtons.setVisibility(View.VISIBLE);
             importanceSlider.setVisibility(View.GONE);
         }
@@ -239,7 +243,7 @@
     }
 
     private void bindToggles(final View importanceButtons, final int importance,
-            final boolean systemApp) {
+            final boolean nonBlockable) {
         ((RadioGroup) importanceButtons).setOnCheckedChangeListener(
                 new RadioGroup.OnCheckedChangeListener() {
                     @Override
@@ -250,7 +254,7 @@
         mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
         mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
         mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
-        if (systemApp) {
+        if (nonBlockable) {
             mBlock.setVisibility(View.GONE);
             mReset.setText(mContext.getString(R.string.do_not_silence));
         } else {
@@ -265,7 +269,7 @@
         }
     }
 
-    private void bindSlider(final View importanceSlider, final boolean systemApp) {
+    private void bindSlider(final View importanceSlider, final boolean nonBlockable) {
         mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
         mInactiveSliderTint = loadColorStateList(R.color.notification_guts_disabled_slider_color);
 
@@ -273,7 +277,7 @@
         mImportanceTitle = ((TextView) importanceSlider.findViewById(R.id.title));
         mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar);
 
-        final int minProgress = systemApp ?
+        final int minProgress = nonBlockable ?
                 NotificationListenerService.Ranking.IMPORTANCE_MIN
                 : NotificationListenerService.Ranking.IMPORTANCE_NONE;
         mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 2c3e805..82867c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -16,7 +16,11 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -26,6 +30,7 @@
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.LatencyTracker;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 
 /**
@@ -37,6 +42,8 @@
     private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
     private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
     private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+    private static final String ACTION_FINGERPRINT_WAKE_FAKE =
+            "com.android.systemui.ACTION_FINGERPRINT_WAKE_FAKE";
 
     /**
      * Mode in which we don't need to wake up the device when we get a fingerprint.
@@ -94,6 +101,8 @@
     private KeyguardViewMediator mKeyguardViewMediator;
     private ScrimController mScrimController;
     private PhoneStatusBar mPhoneStatusBar;
+    private final UnlockMethodCache mUnlockMethodCache;
+    private final Context mContext;
     private boolean mGoingToSleep;
     private int mPendingAuthenticatedUserId = -1;
 
@@ -102,7 +111,9 @@
             DozeScrimController dozeScrimController,
             KeyguardViewMediator keyguardViewMediator,
             ScrimController scrimController,
-            PhoneStatusBar phoneStatusBar) {
+            PhoneStatusBar phoneStatusBar,
+            UnlockMethodCache unlockMethodCache) {
+        mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
         mUpdateMonitor.registerCallback(this);
@@ -111,6 +122,15 @@
         mKeyguardViewMediator = keyguardViewMediator;
         mScrimController = scrimController;
         mPhoneStatusBar = phoneStatusBar;
+        mUnlockMethodCache = unlockMethodCache;
+        if (Build.IS_DEBUGGABLE) {
+            context.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    fakeWakeAndUnlock();
+                }
+            }, new IntentFilter(ACTION_FINGERPRINT_WAKE_FAKE));
+        }
     }
 
     public void setStatusBarKeyguardViewManager(
@@ -139,11 +159,20 @@
         }
     }
 
+    public void fakeWakeAndUnlock() {
+        onFingerprintAcquired();
+        onFingerprintAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
+    }
+
     @Override
     public void onFingerprintAcquired() {
         Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
         releaseFingerprintWakeLock();
         if (!mUpdateMonitor.isDeviceInteractive()) {
+            if (LatencyTracker.isEnabled(mContext)) {
+                LatencyTracker.getInstance(mContext).onActionStart(
+                        LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
+            }
             mWakeLock = mPowerManager.newWakeLock(
                     PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
             Trace.beginSection("acquiring wake-and-unlock");
@@ -263,7 +292,7 @@
                 return MODE_ONLY_WAKE;
             } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
                 return MODE_WAKE_AND_UNLOCK_PULSING;
-            } else if (unlockingAllowed) {
+            } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
                 return MODE_WAKE_AND_UNLOCK;
             } else {
                 return MODE_SHOW_BOUNCER;
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 222e8df..9e5b881 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -43,6 +43,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
@@ -52,7 +53,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public class NavigationBarView extends LinearLayout {
+public class NavigationBarView extends FrameLayout {
     final static boolean DEBUG = false;
     final static String TAG = "StatusBar/NavBarView";
 
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 a6a5742..c6aec73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -32,9 +33,11 @@
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
+import com.android.systemui.DejankUtils;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Interpolators;
+import com.android.systemui.LatencyTracker;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.doze.DozeLog;
@@ -115,6 +118,7 @@
     protected boolean mExpanding;
     private boolean mGestureWaitForTouchSlop;
     private boolean mIgnoreXTouchSlop;
+    private boolean mExpandLatencyTracking;
     private Runnable mPeekRunnable = new Runnable() {
         @Override
         public void run() {
@@ -215,6 +219,14 @@
         }
     }
 
+    public void startExpandLatencyTracking() {
+        if (LatencyTracker.isEnabled(mContext)) {
+            LatencyTracker.getInstance(mContext).onActionStart(
+                    LatencyTracker.ACTION_EXPAND_PANEL);
+            mExpandLatencyTracking = true;
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mInstantExpanding || mTouchDisabled
@@ -739,6 +751,11 @@
     }
 
     public void setExpandedHeightInternal(float h) {
+        if (mExpandLatencyTracking && h != 0f) {
+            DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd(
+                    LatencyTracker.ACTION_EXPAND_PANEL));
+            mExpandLatencyTracking = false;
+        }
         float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
         if (mHeightAnimator == null) {
             float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
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 a07b695..4c40da0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -132,6 +132,7 @@
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Interpolators;
+import com.android.systemui.LatencyTracker;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
@@ -665,6 +666,15 @@
     private boolean mNoAnimationOnNextBarModeChange;
     private FalsingManager mFalsingManager;
 
+    private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onDreamingStateChanged(boolean dreaming) {
+            if (dreaming) {
+                maybeEscalateHeadsUp();
+            }
+        }
+    };
+
     @Override
     public void start() {
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
@@ -702,8 +712,8 @@
         mUnlockMethodCache.addListener(this);
         startKeyguard();
 
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
         mDozeServiceHost = new DozeServiceHost();
-        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost);
         putComponent(DozeHost.class, mDozeServiceHost);
         putComponent(PhoneStatusBar.class, this);
 
@@ -1232,7 +1242,7 @@
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mFingerprintUnlockController = new FingerprintUnlockController(mContext,
                 mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
-                mScrimController, this);
+                mScrimController, this, UnlockMethodCache.getInstance(mContext));
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
                 getBouncerContainer(), mStatusBarWindowManager, mScrimController,
                 mFingerprintUnlockController);
@@ -1291,6 +1301,10 @@
     private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
+            if (LatencyTracker.isEnabled(mContext)) {
+                LatencyTracker.getInstance(mContext).onActionStart(
+                        LatencyTracker.ACTION_TOGGLE_RECENTS);
+            }
             awakenDreams();
             toggleRecentApps();
         }
@@ -4469,6 +4483,7 @@
         }
         if (state == StatusBarState.KEYGUARD) {
             removeRemoteInputEntriesKeptUntilCollapsed();
+            maybeEscalateHeadsUp();
         }
         mState = state;
         mGroupManager.setStatusBarState(state);
@@ -4991,7 +5006,7 @@
         }
     }
 
-    private final class DozeServiceHost extends KeyguardUpdateMonitorCallback implements DozeHost  {
+    private final class DozeServiceHost implements DozeHost {
         // Amount of time to allow to update the time shown on the screen before releasing
         // the wakelock.  This timeout is design to compensate for the fact that we don't
         // currently have a way to know when time display contents have actually been
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 b73e67f..01609e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -30,11 +30,14 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.LatencyTracker;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.RemoteInputController;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+import static com.android.systemui.statusbar.phone.FingerprintUnlockController.*;
 
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
@@ -323,8 +326,7 @@
                 }
             });
         } else {
-            if (mFingerprintUnlockController.getMode()
-                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+            if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING) {
                 mFingerprintUnlockController.startKeyguardFadingAway();
                 mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240);
                 mStatusBarWindowManager.setKeyguardFadingAway(true);
@@ -341,8 +343,7 @@
                 boolean staying = mPhoneStatusBar.hideKeyguard();
                 if (!staying) {
                     mStatusBarWindowManager.setKeyguardFadingAway(true);
-                    if (mFingerprintUnlockController.getMode()
-                            == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+                    if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK) {
                         if (!mScreenTurnedOn) {
                             mDeferScrimFadeOut = true;
                         } else {
@@ -396,6 +397,12 @@
                 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
             }
         }, skipFirstFrame);
+        if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
+                && LatencyTracker.isEnabled(mContext)) {
+            DejankUtils.postAfterTraversal(() ->
+                    LatencyTracker.getInstance(mContext).onActionEnd(
+                            LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK));
+        }
     }
 
     private void executeAfterKeyguardGoneAction() {
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 47ea59e..4425c5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.view.ActionMode;
 import android.view.InputQueue;
@@ -225,6 +226,10 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
+                && mNotificationPanel.getExpandedHeight() == 0f) {
+            mNotificationPanel.startExpandLatencyTracking();
+        }
         mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
         if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
             // Disallow new pointers while the brightness mirror is visible. This is so that you
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index d3ae549..f6c0942 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -381,7 +381,7 @@
     }
 
     public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
-        if (mIsExpanded) {
+        if (mIsExpanded || mBar.isBouncerShowing()) {
             // The touchable region is always the full area when expanded
             return;
         }
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 3c9373b..d7920a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -123,8 +123,10 @@
             mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
         }
         if (mOverflowNumber != null) {
-            mOverflowNumber.layout(getWidth() - mOverflowNumber.getMeasuredWidth(), 0, getWidth(),
-                    mOverflowNumber.getMeasuredHeight());
+            boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+            int left = (isRtl ? 0 : getWidth() - mOverflowNumber.getMeasuredWidth());
+            int right = left + mOverflowNumber.getMeasuredWidth();
+            mOverflowNumber.layout(left, 0, right, mOverflowNumber.getMeasuredHeight());
         }
         if (mNotificationHeader != null) {
             mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
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 c744771..38af030 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3940,6 +3940,7 @@
     private class NotificationSwipeHelper extends SwipeHelper {
         private static final long SHOW_GEAR_DELAY = 60;
         private static final long COVER_GEAR_DELAY = 4000;
+        private static final long SWIPE_GEAR_TIMING = 200;
         private CheckForDrag mCheckForDrag;
         private Runnable mFalsingCheck;
         private Handler mHandler;
@@ -4056,6 +4057,9 @@
 
             boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isIconOnLeft());
             boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
+            final double timeForGesture = ev.getEventTime() - ev.getDownTime();
+            final boolean showGearForSlowOnGoing = !canChildBeDismissed(animView)
+                && timeForGesture >= SWIPE_GEAR_TIMING;
 
             if (mGearSnappedTo && mCurrIconRow.isVisible()) {
                 if (mGearSnappedOnLeft == mCurrIconRow.isIconOnLeft()) {
@@ -4080,7 +4084,8 @@
                 } else {
                     dismissOrSnapBack(animView, velocity, ev);
                 }
-            } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
+            } else if (((!gestureFastEnough || showGearForSlowOnGoing)
+                    && swipedEnoughToShowGear(animView))
                     || gestureTowardsGear) {
                 // Gear has not been snapped to previously and this is gear revealing gesture
                 snapToGear(animView, velocity);
@@ -4132,13 +4137,9 @@
             final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
             final float snapBackThreshold = getSpaceForGear(animView) * multiplier;
             final float translation = getTranslation(animView);
-            final boolean fromLeft = translation > 0;
-            final float absTrans = Math.abs(translation);
-            final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
-
-            return mCurrIconRow.isVisible() && (mCurrIconRow.isIconOnLeft()
-                    ? (translation > snapBackThreshold && translation <= notiThreshold)
-                    : (translation < -snapBackThreshold && translation >= -notiThreshold));
+            return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isIconOnLeft()
+                    ? translation > snapBackThreshold
+                    : translation < -snapBackThreshold);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index ca32567..cd465ac 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -36,16 +36,26 @@
 import com.android.systemui.R;
 
 import java.util.ArrayList;
+import java.util.Iterator;
+
+import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 
 /* Activity for choosing an application for a USB device or accessory */
 public class UsbResolverActivity extends ResolverActivity {
     public static final String TAG = "UsbResolverActivity";
     public static final String EXTRA_RESOLVE_INFOS = "rlist";
+    public static final String EXTRA_RESOLVE_INFO = "rinfo";
 
     private UsbDevice mDevice;
     private UsbAccessory mAccessory;
     private UsbDisconnectedReceiver mDisconnectedReceiver;
 
+    /** Resolve info that switches user profiles */
+    private ResolveInfo mForwardResolveInfo;
+
+    /** The intent that should be started when the profile is switched */
+    private Intent mOtherProfileIntent;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Intent intent = getIntent();
@@ -56,17 +66,22 @@
             return;
         }
         Intent target = (Intent)targetParcelable;
-        ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS);
-        CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
-        super.onCreate(savedInstanceState, target, title, null, rList,
-                true /* Set alwaysUseOption to true to enable "always use this app" checkbox. */ );
+        ArrayList<ResolveInfo> rList = new ArrayList<>(
+                intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS));
 
-        CheckBox alwaysUse = (CheckBox)findViewById(com.android.internal.R.id.alwaysUse);
-        if (alwaysUse != null) {
-            if (mDevice == null) {
-                alwaysUse.setText(R.string.always_use_accessory);
-            } else {
-                alwaysUse.setText(R.string.always_use_device);
+        // The rList contains the apps for all profiles of this users. Separate those. We currently
+        // only support two profiles, i.e. one forward resolve info.
+        ArrayList<ResolveInfo> rListOtherProfile = new ArrayList<>();
+        mForwardResolveInfo = null;
+        for (Iterator<ResolveInfo> iterator = rList.iterator(); iterator.hasNext();) {
+            ResolveInfo ri = iterator.next();
+
+            if (ri.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+                mForwardResolveInfo = ri;
+            } else if (UserHandle.getUserId(ri.activityInfo.applicationInfo.uid)
+                    != UserHandle.myUserId()) {
+                iterator.remove();
+                rListOtherProfile.add(ri);
             }
         }
 
@@ -82,6 +97,40 @@
             }
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
         }
+
+        // Create intent that will be used when switching to other profile. Emulate the behavior of
+        // UsbProfileGroupSettingsManager#resolveActivity
+        if (mForwardResolveInfo != null) {
+            if (rListOtherProfile.size() > 1) {
+                mOtherProfileIntent = new Intent(intent);
+                mOtherProfileIntent.putParcelableArrayListExtra(EXTRA_RESOLVE_INFOS,
+                        rListOtherProfile);
+            } else {
+                mOtherProfileIntent = new Intent(this, UsbConfirmActivity.class);
+                mOtherProfileIntent.putExtra(EXTRA_RESOLVE_INFO, rListOtherProfile.get(0));
+
+                if (mDevice != null) {
+                    mOtherProfileIntent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
+                }
+
+                if (mAccessory != null) {
+                    mOtherProfileIntent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
+                }
+            }
+        }
+
+        CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
+        super.onCreate(savedInstanceState, target, title, null, rList,
+                true /* Set alwaysUseOption to true to enable "always use this app" checkbox. */ );
+
+        CheckBox alwaysUse = (CheckBox)findViewById(com.android.internal.R.id.alwaysUse);
+        if (alwaysUse != null) {
+            if (mDevice == null) {
+                alwaysUse.setText(R.string.always_use_accessory);
+            } else {
+                alwaysUse.setText(R.string.always_use_device);
+            }
+        }
     }
 
     @Override
@@ -95,6 +144,12 @@
     @Override
     protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
         final ResolveInfo ri = target.getResolveInfo();
+        if (ri == mForwardResolveInfo) {
+            startActivityAsUser(mOtherProfileIntent, null,
+                    UserHandle.of(mForwardResolveInfo.targetUserId));
+            return true;
+        }
+
         try {
             IBinder b = ServiceManager.getService(USB_SERVICE);
             IUsbManager service = IUsbManager.Stub.asInterface(b);
@@ -122,7 +177,7 @@
             }
 
             try {
-                target.startAsUser(this, null, new UserHandle(userId));
+                target.startAsUser(this, null, UserHandle.of(userId));
             } catch (ActivityNotFoundException e) {
                 Log.e(TAG, "startActivity failed", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 047085d..8ca277e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -558,35 +558,7 @@
         final VolumeRow activeRow = getActiveRow();
         if (!dismissing) {
             mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT);
-            AutoTransition transition = new AutoTransition();
-            transition.setDuration(mExpandButtonAnimationDuration);
-            transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            transition.addListener(new Transition.TransitionListener() {
-                @Override
-                public void onTransitionStart(Transition transition) {
-                }
-
-                @Override
-                public void onTransitionEnd(Transition transition) {
-                    mWindow.setLayout(
-                            mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
-                }
-
-                @Override
-                public void onTransitionCancel(Transition transition) {
-                }
-
-                @Override
-                public void onTransitionPause(Transition transition) {
-                    mWindow.setLayout(
-                            mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
-                }
-
-                @Override
-                public void onTransitionResume(Transition transition) {
-                }
-            });
-            TransitionManager.beginDelayedTransition(mDialogView, transition);
+            TransitionManager.beginDelayedTransition(mDialogView, getTransistion());
         }
         updateRowsH(activeRow);
         rescheduleTimeoutH();
@@ -702,6 +674,7 @@
         final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF
                 && (mAudioManager.isStreamAffectedByRingerMode(mActiveStream) || mExpanded)
                 && !mZenPanel.isEditing();
+        TransitionManager.beginDelayedTransition(mDialogView, getTransistion());
         if (wasVisible != visible && !visible) {
             prepareForCollapse();
         }
@@ -897,7 +870,7 @@
                 if (row.anim != null) {
                     row.anim.cancel();
                 }
-                row.slider.setProgress(newProgress);
+                row.slider.setProgress(newProgress, true);
             }
         }
     }
@@ -947,6 +920,38 @@
         rescheduleTimeoutH();
     }
 
+    private AutoTransition getTransistion() {
+        AutoTransition transition = new AutoTransition();
+        transition.setDuration(mExpandButtonAnimationDuration);
+        transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        transition.addListener(new Transition.TransitionListener() {
+            @Override
+            public void onTransitionStart(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+                mWindow.setLayout(
+                        mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
+            }
+
+            @Override
+            public void onTransitionCancel(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionPause(Transition transition) {
+                mWindow.setLayout(
+                        mWindow.getAttributes().width, ViewGroup.LayoutParams.WRAP_CONTENT);
+            }
+
+            @Override
+            public void onTransitionResume(Transition transition) {
+            }
+        });
+        return transition;
+    }
+
     private boolean hasTouchFeature() {
         final PackageManager pm = mContext.getPackageManager();
         return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 5d6ac12..23967aa 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -34,6 +34,7 @@
     frameworks/base/packages/SystemUI/res \
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
+    SystemUIPluginLib \
     Keyguard \
     android-support-v7-recyclerview \
     android-support-v7-preference \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 869805e..d943eb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -17,13 +17,17 @@
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
-import android.test.AndroidTestCase;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.MessageQueue;
 import org.junit.Before;
 
 /**
  * Base class that does System UI specific setup.
  */
 public class SysuiTestCase {
+
+    private Handler mHandler;
     protected Context mContext;
 
     @Before
@@ -34,4 +38,65 @@
     protected Context getContext() {
         return mContext;
     }
+
+    protected void waitForIdleSync() {
+        if (mHandler == null) {
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+        waitForIdleSync(mHandler);
+    }
+
+    protected void waitForIdleSync(Handler h) {
+        validateThread(h.getLooper());
+        Idler idler = new Idler(null);
+        h.getLooper().getQueue().addIdleHandler(idler);
+        // Ensure we are non-idle, so the idle handler can run.
+        h.post(new EmptyRunnable());
+        idler.waitForIdle();
+    }
+
+    private static final void validateThread(Looper l) {
+        if (Looper.myLooper() == l) {
+            throw new RuntimeException(
+                "This method can not be called from the looper being synced");
+        }
+    }
+
+    public static final class EmptyRunnable implements Runnable {
+        public void run() {
+        }
+    }
+
+    public static final class Idler implements MessageQueue.IdleHandler {
+        private final Runnable mCallback;
+        private boolean mIdle;
+
+        public Idler(Runnable callback) {
+            mCallback = callback;
+            mIdle = false;
+        }
+
+        @Override
+        public boolean queueIdle() {
+            if (mCallback != null) {
+                mCallback.run();
+            }
+            synchronized (this) {
+                mIdle = true;
+                notifyAll();
+            }
+            return false;
+        }
+
+        public void waitForIdle() {
+            synchronized (this) {
+                while (!mIdle) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
new file mode 100644
index 0000000..ab7de39
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -0,0 +1,284 @@
+/*
+ * 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.plugins;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.PluginInstanceManager.ClassLoaderFactory;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PluginInstanceManagerTest extends SysuiTestCase {
+
+    // Static since the plugin needs to be generated by the PluginInstanceManager using newInstance.
+    private static Plugin sMockPlugin;
+
+    private HandlerThread mHandlerThread;
+    private Context mContextWrapper;
+    private PackageManager mMockPm;
+    private PluginListener mMockListener;
+    private PluginInstanceManager mPluginInstanceManager;
+
+    @Before
+    public void setup() throws Exception {
+        mHandlerThread = new HandlerThread("test_thread");
+        mHandlerThread.start();
+        mContextWrapper = new MyContextWrapper(getContext());
+        mMockPm = mock(PackageManager.class);
+        mMockListener = mock(PluginListener.class);
+        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
+                mMockListener, true, mHandlerThread.getLooper(), 1, true,
+                new TestClassLoaderFactory());
+        sMockPlugin = mock(Plugin.class);
+        when(sMockPlugin.getVersion()).thenReturn(1);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        sMockPlugin = null;
+    }
+
+    @Test
+    public void testNoPlugins() {
+        when(mMockPm.queryIntentServices(Mockito.any(), Mockito.anyInt())).thenReturn(
+                Collections.emptyList());
+        mPluginInstanceManager.startListening();
+
+        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+
+        verify(mMockListener, Mockito.never()).onPluginConnected(
+                ArgumentCaptor.forClass(Plugin.class).capture());
+    }
+
+    @Test
+    public void testPluginCreate() {
+        createPlugin();
+
+        // Verify startup lifecycle
+        verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+                ArgumentCaptor.forClass(Context.class).capture());
+        verify(mMockListener).onPluginConnected(ArgumentCaptor.forClass(Plugin.class).capture());
+    }
+
+    @Test
+    public void testPluginDestroy() {
+        createPlugin(); // Get into valid created state.
+
+        mPluginInstanceManager.stopListening();
+
+        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+
+        // Verify shutdown lifecycle
+        verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(sMockPlugin).onDestroy();
+    }
+
+    @Test
+    public void testIncorrectVersion() {
+        setupFakePmQuery();
+        when(sMockPlugin.getVersion()).thenReturn(2);
+
+        mPluginInstanceManager.startListening();
+
+        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+
+        // Plugin shouldn't be connected because it is the wrong version.
+        verify(mMockListener, Mockito.never()).onPluginConnected(
+                ArgumentCaptor.forClass(Plugin.class).capture());
+    }
+
+    @Test
+    public void testReloadOnChange() {
+        createPlugin(); // Get into valid created state.
+
+        // Send a package changed broadcast.
+        Intent i = new Intent(Intent.ACTION_PACKAGE_CHANGED,
+                Uri.fromParts("package", "com.android.systemui", null));
+        mPluginInstanceManager.onReceive(mContextWrapper, i);
+
+        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+
+        // Verify the old one was destroyed.
+        verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(sMockPlugin).onDestroy();
+        // Also verify we got a second onCreate.
+        verify(sMockPlugin, Mockito.times(2)).onCreate(
+                ArgumentCaptor.forClass(Context.class).capture(),
+                ArgumentCaptor.forClass(Context.class).capture());
+        verify(mMockListener, Mockito.times(2)).onPluginConnected(
+                ArgumentCaptor.forClass(Plugin.class).capture());
+    }
+
+    @Test
+    public void testNonDebuggable() {
+        // Create a version that thinks the build is not debuggable.
+        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
+                mMockListener, true, mHandlerThread.getLooper(), 1, false,
+                new TestClassLoaderFactory());
+        setupFakePmQuery();
+
+        mPluginInstanceManager.startListening();
+
+        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+        waitForIdleSync(mPluginInstanceManager.mMainHandler);;
+
+        // Non-debuggable build should receive no plugins.
+        verify(mMockListener, Mockito.never()).onPluginConnected(
+                ArgumentCaptor.forClass(Plugin.class).capture());
+    }
+
+    @Test
+    public void testCheckAndDisable() {
+        createPlugin(); // Get into valid created state.
+
+        // Start with an unrelated class.
+        boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName());
+        assertFalse(result);
+        verify(mMockPm, Mockito.never()).setComponentEnabledSetting(
+                ArgumentCaptor.forClass(ComponentName.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture());
+
+        // Now hand it a real class and make sure it disables the plugin.
+        result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName());
+        assertTrue(result);
+        verify(mMockPm).setComponentEnabledSetting(
+                ArgumentCaptor.forClass(ComponentName.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture());
+    }
+
+    @Test
+    public void testDisableAll() {
+        createPlugin(); // Get into valid created state.
+
+        mPluginInstanceManager.disableAll();
+
+        verify(mMockPm).setComponentEnabledSetting(
+                ArgumentCaptor.forClass(ComponentName.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture());
+    }
+
+    private void setupFakePmQuery() {
+        List<ResolveInfo> list = new ArrayList<>();
+        ResolveInfo info = new ResolveInfo();
+        info.serviceInfo = new ServiceInfo();
+        info.serviceInfo.packageName = "com.android.systemui";
+        info.serviceInfo.name = TestPlugin.class.getName();
+        list.add(info);
+        when(mMockPm.queryIntentServices(Mockito.any(), Mockito.anyInt())).thenReturn(list);
+
+        when(mMockPm.checkPermission(Mockito.anyString(), Mockito.anyString())).thenReturn(
+                PackageManager.PERMISSION_GRANTED);
+
+        try {
+            ApplicationInfo appInfo = getContext().getApplicationInfo();
+            when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
+                    appInfo);
+        } catch (NameNotFoundException e) {
+            // Shouldn't be possible, but if it is, we want to fail.
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void createPlugin() {
+        setupFakePmQuery();
+
+        mPluginInstanceManager.startListening();
+
+        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
+        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+    }
+
+    private static class TestClassLoaderFactory extends ClassLoaderFactory {
+        @Override
+        public ClassLoader createClassLoader(String path, ClassLoader base) {
+            return base;
+        }
+    }
+
+    // Real context with no registering/unregistering of receivers.
+    private static class MyContextWrapper extends ContextWrapper {
+        public MyContextWrapper(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+            return null;
+        }
+
+        @Override
+        public void unregisterReceiver(BroadcastReceiver receiver) {
+        }
+    }
+
+    public static class TestPlugin implements Plugin {
+        @Override
+        public int getVersion() {
+            return sMockPlugin.getVersion();
+        }
+
+        @Override
+        public void onCreate(Context sysuiContext, Context pluginContext) {
+            sMockPlugin.onCreate(sysuiContext, pluginContext);
+        }
+
+        @Override
+        public void onDestroy() {
+            sMockPlugin.onDestroy();
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
new file mode 100644
index 0000000..56e742a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.plugins;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.PluginManager.PluginInstanceManagerFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PluginManagerTest extends SysuiTestCase {
+
+    private PluginInstanceManagerFactory mMockFactory;
+    private PluginInstanceManager mMockPluginInstance;
+    private PluginManager mPluginManager;
+    private PluginListener mMockListener;
+
+    private UncaughtExceptionHandler mRealExceptionHandler;
+    private UncaughtExceptionHandler mMockExceptionHandler;
+    private UncaughtExceptionHandler mPluginExceptionHandler;
+
+    @Before
+    public void setup() throws Exception {
+        mRealExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+        mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
+        mMockFactory = mock(PluginInstanceManagerFactory.class);
+        mMockPluginInstance = mock(PluginInstanceManager.class);
+        when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
+                Mockito.anyBoolean(), Mockito.any(), Mockito.anyInt()))
+                .thenReturn(mMockPluginInstance);
+        mPluginManager = new PluginManager(getContext(), mMockFactory, true, mMockExceptionHandler);
+        resetExceptionHandler();
+        mMockListener = mock(PluginListener.class);
+    }
+
+    @Test
+    public void testAddListener() {
+        mPluginManager.addPluginListener("myAction", mMockListener, 1);
+
+        verify(mMockPluginInstance).startListening();
+    }
+
+    @Test
+    public void testRemoveListener() {
+        mPluginManager.addPluginListener("myAction", mMockListener, 1);
+
+        mPluginManager.removePluginListener(mMockListener);
+        verify(mMockPluginInstance).stopListening();
+    }
+
+    @Test
+    public void testNonDebuggable() {
+        mPluginManager = new PluginManager(getContext(), mMockFactory, false,
+                mMockExceptionHandler);
+        resetExceptionHandler();
+        mPluginManager.addPluginListener("myAction", mMockListener, 1);
+
+        verify(mMockPluginInstance, Mockito.never()).startListening();
+    }
+
+    @Test
+    public void testExceptionHandler_foundPlugin() {
+        mPluginManager.addPluginListener("myAction", mMockListener, 1);
+        when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(true);
+
+        mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
+
+        verify(mMockPluginInstance, Mockito.atLeastOnce()).checkAndDisable(
+                ArgumentCaptor.forClass(String.class).capture());
+        verify(mMockPluginInstance, Mockito.never()).disableAll();
+        verify(mMockExceptionHandler).uncaughtException(
+                ArgumentCaptor.forClass(Thread.class).capture(),
+                ArgumentCaptor.forClass(Throwable.class).capture());
+    }
+
+    @Test
+    public void testExceptionHandler_noFoundPlugin() {
+        mPluginManager.addPluginListener("myAction", mMockListener, 1);
+        when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(false);
+
+        mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
+
+        verify(mMockPluginInstance, Mockito.atLeastOnce()).checkAndDisable(
+                ArgumentCaptor.forClass(String.class).capture());
+        verify(mMockPluginInstance).disableAll();
+        verify(mMockExceptionHandler).uncaughtException(
+                ArgumentCaptor.forClass(Thread.class).capture(),
+                ArgumentCaptor.forClass(Throwable.class).capture());
+    }
+
+    private void resetExceptionHandler() {
+        mPluginExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+        // Set back the real exception handler so the test can crash if it wants to.
+        Thread.setDefaultUncaughtExceptionHandler(mRealExceptionHandler);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
new file mode 100644
index 0000000..39b6412
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.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.systemui.power;
+
+import static android.test.MoreAsserts.assertNotEqual;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PowerNotificationWarningsTest {
+    private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
+    private PowerNotificationWarnings mPowerNotificationWarnings;
+
+    @Before
+    public void setUp() throws Exception {
+        // Test Instance.
+        mPowerNotificationWarnings = new PowerNotificationWarnings(
+                InstrumentationRegistry.getTargetContext(), mMockNotificationManager, null);
+    }
+
+    @Test
+    public void testIsInvalidChargerWarningShowing_DefaultsToFalse() {
+        assertFalse(mPowerNotificationWarnings.isInvalidChargerWarningShowing());
+    }
+
+    @Test
+    public void testIsInvalidChargerWarningShowing_TrueAfterShow() {
+        mPowerNotificationWarnings.showInvalidChargerWarning();
+        assertTrue(mPowerNotificationWarnings.isInvalidChargerWarningShowing());
+    }
+
+    @Test
+    public void testIsInvalidChargerWarningShowing_FalseAfterDismiss() {
+        mPowerNotificationWarnings.showInvalidChargerWarning();
+        mPowerNotificationWarnings.dismissInvalidChargerWarning();
+        assertFalse(mPowerNotificationWarnings.isInvalidChargerWarningShowing());
+    }
+
+    @Test
+    public void testShowInvalidChargerNotification_NotifyAsUser() {
+        mPowerNotificationWarnings.showInvalidChargerWarning();
+        verify(mMockNotificationManager, times(1))
+                .notifyAsUser(anyString(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testDismissInvalidChargerNotification_CancelAsUser() {
+        mPowerNotificationWarnings.showInvalidChargerWarning();
+        mPowerNotificationWarnings.dismissInvalidChargerWarning();
+        verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), anyInt(), any());
+    }
+
+    @Test
+    public void testShowLowBatteryNotification_NotifyAsUser() {
+        mPowerNotificationWarnings.showLowBatteryWarning(false);
+        verify(mMockNotificationManager, times(1))
+                .notifyAsUser(anyString(), anyInt(), any(), any());
+    }
+
+    @Test
+    public void testDismissLowBatteryNotification_CancelAsUser() {
+        mPowerNotificationWarnings.showLowBatteryWarning(false);
+        mPowerNotificationWarnings.dismissLowBatteryWarning();
+        verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), anyInt(), any());
+    }
+
+    @Test
+    public void testShowLowBatteryNotification_Silent() {
+        mPowerNotificationWarnings.showLowBatteryWarning(false);
+        ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class);
+        verify(mMockNotificationManager)
+                .notifyAsUser(anyString(), anyInt(), captor.capture(), any());
+        assertEquals(null, captor.getValue().sound);
+    }
+
+    @Test
+    public void testShowLowBatteryNotification_Sound() {
+        mPowerNotificationWarnings.showLowBatteryWarning(true);
+        ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class);
+        verify(mMockNotificationManager)
+                .notifyAsUser(anyString(), anyInt(), captor.capture(), any());
+        assertNotEqual(null, captor.getValue().sound);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 2bfbc5f..d7ff04f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -15,12 +15,14 @@
  */
 package com.android.systemui.qs.external;
 
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Service;
@@ -29,6 +31,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
@@ -43,55 +46,74 @@
 import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.Tile;
 import android.service.quicksettings.TileService;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 import android.util.Log;
-import com.android.systemui.SysuiTestCase;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class TileLifecycleManagerTest extends SysuiTestCase {
-    public static final String TILE_UPDATE_BROADCAST = "com.android.systemui.tests.TILE_UPDATE";
-    public static final String EXTRA_CALLBACK = "callback";
+public class TileLifecycleManagerTest {
+    private static final int TEST_FAIL_TIMEOUT = 5000;
 
+    private final Context mMockContext = Mockito.mock(Context.class);
+    private final PackageManagerAdapter mMockPackageManagerAdapter =
+            Mockito.mock(PackageManagerAdapter.class);
+    private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class);
+    private ComponentName mTileServiceComponentName;
+    private Intent mTileServiceIntent;
+    private UserHandle mUser;
     private HandlerThread mThread;
     private Handler mHandler;
     private TileLifecycleManager mStateManager;
-    private final Object mBroadcastLock = new Object();
-    private final ArraySet<String> mCallbacks = new ArraySet<>();
-    private final PackageManagerAdapter mMockPackageManagerAdapter =
-            Mockito.mock(PackageManagerAdapter.class);
-    private boolean mBound;
 
     @Before
     public void setUp() throws Exception {
         setPackageEnabled(true);
+        mTileServiceComponentName = new ComponentName(
+                InstrumentationRegistry.getTargetContext(), "FakeTileService.class");
+
+        // Stub.asInterface will just return itself.
+        when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService);
+
+        // Default behavior for bind is success and connects as mMockTileService.
+        when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
+                .thenAnswer(
+                        new Answer<Boolean>() {
+                            @Override
+                            public Boolean answer(InvocationOnMock invocation) {
+                                ServiceConnection connection =
+                                        (ServiceConnection) invocation.getArguments()[1];
+                                connection.onServiceConnected(
+                                        mTileServiceComponentName, mMockTileService);
+                                return true;
+                            }
+                        });
+
+
+        mTileServiceIntent = new Intent().setComponent(mTileServiceComponentName);
+        mUser = new UserHandle(UserHandle.myUserId());
         mThread = new HandlerThread("TestThread");
         mThread.start();
         mHandler = new Handler(mThread.getLooper());
-        ComponentName component = new ComponentName(getContext(), FakeTileService.class);
-        mStateManager = new TileLifecycleManager(mHandler, getContext(),
+        mStateManager = new TileLifecycleManager(mHandler, mMockContext,
                 Mockito.mock(IQSService.class), new Tile(),
-                new Intent().setComponent(component),
-                new UserHandle(UserHandle.myUserId()),
+                mTileServiceIntent,
+                mUser,
                 mMockPackageManagerAdapter);
-        mCallbacks.clear();
-        getContext().registerReceiver(mReceiver, new IntentFilter(TILE_UPDATE_BROADCAST));
     }
 
     @After
     public void tearDown() throws Exception {
-        if (mBound) {
-            unbindService();
-        }
         mThread.quit();
-        getContext().unregisterReceiver(mReceiver);
     }
 
     private void setPackageEnabled(boolean enabled) throws Exception {
@@ -110,260 +132,134 @@
                 .thenReturn(defaultPackageInfo);
     }
 
-    @Test
-    public void testSync() {
-        syncWithHandler();
+    private void verifyBind(int times) {
+        verify(mMockContext, times(times)).bindServiceAsUser(
+                mTileServiceIntent,
+                mStateManager,
+                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                mUser);
     }
 
     @Test
     public void testBind() {
-        bindService();
-        waitForCallback("onCreate");
+        mStateManager.setBindService(true);
+        verifyBind(1);
     }
 
     @Test
     public void testUnbind() {
-        bindService();
-        waitForCallback("onCreate");
-        unbindService();
-        waitForCallback("onDestroy");
+        mStateManager.setBindService(true);
+        mStateManager.setBindService(false);
+        verify(mMockContext).unbindService(mStateManager);
     }
 
     @Test
-    public void testTileServiceCallbacks() {
-        bindService();
-        waitForCallback("onCreate");
-
+    public void testTileServiceCallbacks() throws Exception {
+        mStateManager.setBindService(true);
         mStateManager.onTileAdded();
-        waitForCallback("onTileAdded");
+        verify(mMockTileService).onTileAdded();
         mStateManager.onStartListening();
-        waitForCallback("onStartListening");
+        verify(mMockTileService).onStartListening();
         mStateManager.onClick(null);
-        waitForCallback("onClick");
+        verify(mMockTileService).onClick(null);
         mStateManager.onStopListening();
-        waitForCallback("onStopListening");
+        verify(mMockTileService).onStopListening();
         mStateManager.onTileRemoved();
-        waitForCallback("onTileRemoved");
-
-        unbindService();
+        verify(mMockTileService).onTileRemoved();
     }
 
     @Test
-    public void testAddedBeforeBind() {
+    public void testAddedBeforeBind() throws Exception {
         mStateManager.onTileAdded();
+        mStateManager.setBindService(true);
 
-        bindService();
-        waitForCallback("onCreate");
-        waitForCallback("onTileAdded");
+        verifyBind(1);
+        verify(mMockTileService).onTileAdded();
     }
 
     @Test
-    public void testListeningBeforeBind() {
+    public void testListeningBeforeBind() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
+        mStateManager.setBindService(true);
 
-        bindService();
-        waitForCallback("onCreate");
-        waitForCallback("onTileAdded");
-        waitForCallback("onStartListening");
+        verifyBind(1);
+        verify(mMockTileService).onTileAdded();
+        verify(mMockTileService).onStartListening();
     }
 
     @Test
-    public void testClickBeforeBind() {
+    public void testClickBeforeBind() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
         mStateManager.onClick(null);
+        mStateManager.setBindService(true);
 
-        bindService();
-        waitForCallback("onCreate");
-        waitForCallback("onTileAdded");
-        waitForCallback("onStartListening");
-        waitForCallback("onClick");
+        verifyBind(1);
+        verify(mMockTileService).onTileAdded();
+        verify(mMockTileService).onStartListening();
+        verify(mMockTileService).onClick(null);
     }
 
     @Test
-    public void testListeningNotListeningBeforeBind() {
+    public void testListeningNotListeningBeforeBind() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
         mStateManager.onStopListening();
+        mStateManager.setBindService(true);
 
-        bindService();
-        waitForCallback("onCreate");
-        unbindService();
-        waitForCallback("onDestroy");
-        assertFalse(mCallbacks.contains("onStartListening"));
+        verifyBind(1);
+        mStateManager.setBindService(false);
+        verify(mMockContext).unbindService(mStateManager);
+        verify(mMockTileService, never()).onStartListening();
     }
 
     @Test
-    public void testNoClickOfNotListeningAnymore() {
+    public void testNoClickOfNotListeningAnymore() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
         mStateManager.onClick(null);
         mStateManager.onStopListening();
+        mStateManager.setBindService(true);
 
-        bindService();
-        waitForCallback("onCreate");
-        unbindService();
-        waitForCallback("onDestroy");
-        assertFalse(mCallbacks.contains("onClick"));
+        verifyBind(1);
+        mStateManager.setBindService(false);
+        verify(mMockContext).unbindService(mStateManager);
+        verify(mMockTileService, never()).onClick(null);
     }
 
     @Test
     public void testComponentEnabling() throws Exception {
         mStateManager.onTileAdded();
         mStateManager.onStartListening();
-
         setPackageEnabled(false);
-        bindService();
-        // Package not available, should be listening for package changes.
-        assertTrue(mStateManager.mReceiverRegistered);
+        mStateManager.setBindService(true);
+        // Package not available, not yet created.
+        verifyBind(0);
 
         // Package is re-enabled.
         setPackageEnabled(true);
         mStateManager.onReceive(
-                mContext,
+                mMockContext,
                 new Intent(
                         Intent.ACTION_PACKAGE_CHANGED,
-                        Uri.fromParts("package", getContext().getPackageName(), null)));
-        waitForCallback("onCreate");
+                        Uri.fromParts(
+                                "package", mTileServiceComponentName.getPackageName(), null)));
+        verifyBind(1);
     }
 
     @Test
-    public void testKillProcess() {
+    public void testKillProcess() throws Exception {
         mStateManager.onStartListening();
-        bindService();
-        waitForCallback("onCreate");
-        waitForCallback("onStartListening");
-
-        getContext().sendBroadcast(new Intent(FakeTileService.ACTION_KILL));
-
-        waitForCallback("onCreate");
-        waitForCallback("onStartListening");
-    }
-
-    private void bindService() {
-        mBound = true;
         mStateManager.setBindService(true);
-    }
+        mStateManager.setBindRetryDelay(0);
+        mStateManager.onServiceDisconnected(mTileServiceComponentName);
 
-    private void unbindService() {
-        mBound = false;
-        mStateManager.setBindService(false);
-    }
+        // Guarantees mHandler has processed all messages.
+        assertTrue(mHandler.runWithScissors(()->{}, TEST_FAIL_TIMEOUT));
 
-    private void waitForCallback(String callback) {
-        for (int i = 0; i < 50; i++) {
-            if (mCallbacks.contains(callback)) {
-                mCallbacks.remove(callback);
-                return;
-            }
-            synchronized (mBroadcastLock) {
-                try {
-                    mBroadcastLock.wait(500);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-        if (mCallbacks.contains(callback)) {
-            mCallbacks.remove(callback);
-            return;
-        }
-        fail("Didn't receive callback: " + callback);
-    }
-
-    private void syncWithHandler() {
-        final Object lock = new Object();
-        synchronized (lock) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    synchronized (lock) {
-                        lock.notify();
-                    }
-                }
-            });
-            try {
-                lock.wait(10000);
-            } catch (InterruptedException e) {
-            }
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mCallbacks.add(intent.getStringExtra(EXTRA_CALLBACK));
-            synchronized (mBroadcastLock) {
-                mBroadcastLock.notify();
-            }
-        }
-    };
-
-    public static class FakeTileService extends Service {
-        public static final String ACTION_KILL = "com.android.systemui.test.KILL";
-
-        @Override
-        public IBinder onBind(Intent intent) {
-            return new IQSTileService.Stub() {
-                @Override
-                public void onTileAdded() throws RemoteException {
-                    sendCallback("onTileAdded");
-                }
-
-                @Override
-                public void onTileRemoved() throws RemoteException {
-                    sendCallback("onTileRemoved");
-                }
-
-                @Override
-                public void onStartListening() throws RemoteException {
-                    sendCallback("onStartListening");
-                }
-
-                @Override
-                public void onStopListening() throws RemoteException {
-                    sendCallback("onStopListening");
-                }
-
-                @Override
-                public void onClick(IBinder iBinder) throws RemoteException {
-                    sendCallback("onClick");
-                }
-
-                @Override
-                public void onUnlockComplete() throws RemoteException {
-                    sendCallback("onUnlockComplete");
-                }
-            };
-        }
-
-        @Override
-        public void onCreate() {
-            super.onCreate();
-            registerReceiver(mReceiver, new IntentFilter(ACTION_KILL));
-            sendCallback("onCreate");
-        }
-
-        @Override
-        public void onDestroy() {
-            super.onDestroy();
-            unregisterReceiver(mReceiver);
-            sendCallback("onDestroy");
-        }
-
-        private void sendCallback(String callback) {
-            Log.d("TileLifecycleManager", "Relaying: " + callback);
-            sendBroadcast(new Intent(TILE_UPDATE_BROADCAST)
-                    .putExtra(EXTRA_CALLBACK, callback));
-        }
-
-        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (ACTION_KILL.equals(intent.getAction())) {
-                    Process.killProcess(Process.myPid());
-                }
-            }
-        };
+        // Two calls: one for the first bind, one for the restart.
+        verifyBind(2);
+        verify(mMockTileService, times(2)).onStartListening();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java
deleted file mode 100644
index 04e8915..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2011 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.screenshot;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Environment;
-import android.os.FileObserver;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.view.KeyEvent;
-import com.android.systemui.screenshot.ScreenshotStubActivity;
-import java.io.File;
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertNotNull;
-
-/**
- * Functional tests for the global screenshot feature.
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ScreenshotTest {
-
-    private static final String LOG_TAG = "ScreenshotTest";
-    private static final int SCREEN_WAIT_TIME_SEC = 5;
-
-    public ScreenshotTest() {}
-
-    @Rule
-    public ActivityTestRule<ScreenshotStubActivity> mActivityRule =
-            new ActivityTestRule<>(ScreenshotStubActivity.class);
-
-    /**
-     * A simple test for screenshots that launches an Activity, injects the key event combo
-     * to trigger the screenshot, and verifies the screenshot was taken successfully.
-     */
-    @Test
-    public void testScreenshot() throws Exception {
-        if (true) {
-            // Disable until this works again.
-            return;
-        }
-        Log.d(LOG_TAG, "starting testScreenshot");
-        // launch the activity.
-        ScreenshotStubActivity activity = mActivityRule.getActivity();
-        assertNotNull(activity);
-
-        File screenshotDir = getScreenshotDir();
-        NewScreenshotObserver observer = new NewScreenshotObserver(
-                screenshotDir.getAbsolutePath());
-        observer.startWatching();
-        takeScreenshot();
-        // unlikely, but check if a new screenshot file was already created
-        if (observer.getCreatedPath() == null) {
-            // wait for screenshot to be created
-            synchronized(observer) {
-                observer.wait(SCREEN_WAIT_TIME_SEC*1000);
-            }
-        }
-        assertNotNull(String.format("Could not find screenshot after %d seconds",
-                SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());
-
-        File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
-        try {
-            assertTrue(String.format("Detected new screenshot %s but its not a file",
-                    screenshotFile.getName()), screenshotFile.isFile());
-            assertTrue(String.format("Detected new screenshot %s but its not an image",
-                    screenshotFile.getName()), isValidImage(screenshotFile));
-        } finally {
-            // delete the file to prevent external storage from filing up
-            screenshotFile.delete();
-        }
-    }
-
-    private static class NewScreenshotObserver extends FileObserver {
-        private String mAddedPath = null;
-
-        NewScreenshotObserver(String path) {
-            super(path, FileObserver.CREATE);
-        }
-
-        synchronized String getCreatedPath() {
-            return mAddedPath;
-        }
-
-        @Override
-        public void onEvent(int event, String path) {
-            Log.d(LOG_TAG, String.format("Detected new file added %s", path));
-            synchronized (this) {
-                mAddedPath = path;
-                notify();
-            }
-        }
-    }
-
-    /**
-     * Inject the key sequence to take a screenshot.
-     */
-    private void takeScreenshot() {
-        InstrumentationRegistry.getInstrumentation()
-                .sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_POWER));
-        InstrumentationRegistry.getInstrumentation()
-                .sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN));
-        // the volume down key event will cause the 'volume adjustment' UI to appear in the
-        // foreground, and steal UI focus
-        // unfortunately this means the next key event will get directed to the
-        // 'volume adjustment' UI, instead of this test's activity
-        // for this reason this test must be signed with platform certificate, to grant this test
-        // permission to inject key events to another process
-        InstrumentationRegistry.getInstrumentation()
-                .sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_DOWN));
-        InstrumentationRegistry.getInstrumentation()
-                .sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_POWER));
-    }
-
-    /**
-     * Get the directory where screenshot images are stored.
-     */
-    private File getScreenshotDir() {
-        // TODO: get this dir location from a constant
-        return new File(Environment.getExternalStorageDirectory(), "Pictures" + File.separator +
-                "Screenshots");
-    }
-
-    /**
-     * Return true if file is valid image file
-     */
-    private boolean isValidImage(File screenshotFile) {
-        Bitmap b = BitmapFactory.decodeFile(screenshotFile.getAbsolutePath());
-        // TODO: do more checks on image
-        return b != null;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index c51cef0..6c9cfe0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -37,6 +37,9 @@
 import com.android.systemui.SysuiTestCase;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
@@ -76,6 +79,19 @@
 
     private NetworkCapabilities mNetCapabilities;
 
+    @Rule
+    public TestWatcher failWatcher = new TestWatcher() {
+        @Override
+        protected void failed(Throwable e, Description description) {
+            // Print out mNetworkController state if the test fails.
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            mNetworkController.dump(null, pw, null);
+            pw.flush();
+            Log.d(TAG, sw.toString());
+        }
+    };
+
     @Before
     public void setUp() throws Exception {
         mMockWm = mock(WifiManager.class);
@@ -147,15 +163,6 @@
 
     }
 
-    @After
-    public void tearDown() throws Exception {
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
-        mNetworkController.dump(null, pw, null);
-        pw.flush();
-        Log.d(TAG, sw.toString());
-    }
-
     // 2 Bars 3G GSM.
     public void setupDefaultSignal() {
         setIsGsm(true);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 00707e4..5890785 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2279,6 +2279,186 @@
     // OPEN: Settings > Security > Device administrators
     DEVICE_ADMIN_SETTINGS = 516;
 
+    // ACTION: Managed provisioning was launched to set this package as DPC app.
+    // PACKAGE: DPC's package name.
+    PROVISIONING_DPC_PACKAGE_NAME = 517;
+
+    // ACTION: Managed provisioning triggered DPC installation.
+    // PACKAGE: Package name of package which installed DPC.
+    PROVISIONING_DPC_INSTALLED_BY_PACKAGE = 518;
+
+    // ACTION: Logged when provisioning activity finishes.
+    // TIME: Indicates time taken by provisioning activity to finish in MS.
+    PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS = 519;
+
+    // ACTION: Logged when preprovisioning activity finishes.
+    // TIME: Indicates time taken by preprovisioning activity to finish in MS.
+    PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 520;
+
+    // ACTION: Logged when encrypt device activity finishes.
+    // TIME: Indicates time taken by encrypt device activity to finish in MS.
+    PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 521;
+
+    // ACTION: Logged when web activity finishes.
+    // TIME: Indicates total time taken by web activity to finish in MS.
+    PROVISIONING_WEB_ACTIVITY_TIME_MS = 522;
+
+    // ACTION: Logged when trampoline activity finishes.
+    // TIME: Indicates total time taken by trampoline activity to finish in MS.
+    PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523;
+
+    // ACTION: Logged when encryption activity finishes.
+    // TIME: Indicates total time taken by post encryption activity to finish in MS.
+    PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524;
+
+    // ACTION: Logged when finalization activity finishes.
+    // TIME: Indicates time taken by finalization activity to finish in MS.
+    PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525;
+
+    // OPEN: Settings Support > Phone/Chat -> Disclaimer
+    DIALOG_SUPPORT_DISCLAIMER = 526;
+
+    // OPEN: Settings Support > Travel abroad
+    DIALOG_SUPPORT_PHONE = 527;
+
+    // OPEN: Settings > Security > Factory Reset Protection dialog
+    DIALOG_FRP = 528;
+
+    // OPEN: Settings > Custom list preference with confirmation message
+    DIALOG_CUSTOM_LIST_CONFIRMATION = 529;
+
+    // OPEN: Settings > APN Editor > Error dialog
+    DIALOG_APN_EDITOR_ERROR = 530;
+
+    // OPEN: Settings > Users > Edit owner info dialog
+    DIALOG_OWNER_INFO_SETTINGS = 531;
+
+    // OPEN: Settings > Security > Use one lock dialog
+    DIALOG_UNIFICATION_CONFIRMATION = 532;
+
+    // OPEN: Settings > Security > User Credential
+    DIALOG_USER_CREDENTIAL = 533;
+
+    // OPEN: Settings > Accounts > Remove account
+    DIALOG_REMOVE_USER = 534;
+
+    // OPEN: Settings > Accounts > Confirm auto sync dialog
+    DIALOG_CONFIRM_AUTO_SYNC_CHANGE = 535;
+
+    // OPEN: Settings > Apps > Dialog for running service details
+    DIALOG_RUNNIGN_SERVICE = 536;
+
+    // OPEN: Settings > Dialog for hiding home settings
+    DIALOG_NO_HOME = 537;
+
+    // OPEN: Settings > Bluetooth > Rename this device
+    DIALOG_BLUETOOTH_RENAME = 538;
+
+    // OPEN: Settings > Bluetooth > Paired device profile
+    DIALOG_BLUETOOTH_PAIRED_DEVICE_PROFILE = 539;
+
+    // OPEN: Settings > Battery optimization > details for app
+    DIALOG_HIGH_POWER_DETAILS = 540;
+
+    // OPEN: Settings > Keyboard > Show keyboard layout dialog
+    DIALOG_KEYBOARD_LAYOUT = 541;
+
+    // OPEN: Settings > Wifi > WPS Setup dialog
+    DIALOG_WPS_SETUP = 542;
+
+    // OPEN: Settings > WIFI Scan permission dialog
+    DIALOG_WIFI_SCAN_MODE = 543;
+
+    // OPEN: Settings > WIFI Setup > Skip Wifi dialog
+    DIALOG_WIFI_SKIP = 544;
+
+    // OPEN: Settings > Wireless > VPN > Config dialog
+    DIALOG_LEGACY_VPN_CONFIG = 545;
+
+    // OPEN: Settings > Wireless > VPN > Config dialog for app
+    DIALOG_VPN_APP_CONFIG = 546;
+
+    // OPEN: Settings > Wireless > VPN > Cannot connect dialog
+    DIALOG_VPN_CANNOT_CONNECT = 547;
+
+    // OPEN: Settings > Wireless > VPN > Replace existing VPN dialog
+    DIALOG_VPN_REPLACE_EXISTING = 548;
+
+    // OPEN: Settings > Billing cycle > Edit billing cycle dates dialog
+    DIALOG_BILLING_CYCLE = 549;
+
+    // OPEN: Settings > Billing cycle > Edit data limit/warning dialog
+    DIALOG_BILLING_BYTE_LIMIT = 550;
+
+    // OPEN: Settings > Billing cycle > turn on data limit dialog
+    DIALOG_BILLING_CONFIRM_LIMIT = 551;
+
+    // OPEN: Settings > Service > Turn off notification access dialog
+    DIALOG_DISABLE_NOTIFICATION_ACCESS = 552;
+
+    // OPEN: Settings > Sound > Use personal sound for work profile dialog
+    DIALOG_UNIFY_SOUND_SETTINGS = 553;
+
+    // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being granted.
+    DIALOG_ZEN_ACCESS_GRANT = 554;
+
+    // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being revoked.
+    DIALOG_ZEN_ACCESS_REVOKE = 555;
+
+    // OPEN: Settings > Zen mode > Dialog that picks time for zen mode.
+    DIALOG_ZEN_TIMEPICKER = 556;
+
+    // OPEN: Settings > Apps > Dialog that informs user to allow service access for app.
+    DIALOG_SERVICE_ACCESS_WARNING = 557;
+
+    // OPEN: Settings > Apps > Dialog for app actions (such as force stop/clear data)
+    DIALOG_APP_INFO_ACTION = 558;
+
+    // OPEN: Settings > Storage > Dialog for forgetting a storage device
+    DIALOG_VOLUME_FORGET = 559;
+
+    // OPEN: Settings > Storage > Dialog warning that a volume is slow
+    DIALOG_VOLUME_SLOW_WARNING = 560;
+
+    // OPEN: Settings > Storage > Dialog for initializing a volume
+    DIALOG_VOLUME_INIT = 561;
+
+    // OPEN: Settings > Storage > Dialog for unmounting a volume
+    DIALOG_VOLUME_UNMOUNT = 562;
+
+    // OPEN: Settings > Storage > Dialog for renaming a volume
+    DIALOG_VOLUME_RENAME = 563;
+
+    // OPEN: Settings > Storage > Dialog for clear cache
+    DIALOG_STORAGE_CLEAR_CACHE = 564;
+
+    // OPEN: Settings > Storage > Dialog for system info
+    DIALOG_STORAGE_SYSTEM_INFO = 565;
+
+    // OPEN: Settings > Storage > Dialog for other info
+    DIALOG_STORAGE_OTHER_INFO = 566;
+
+    // OPEN: Settings > Storage > Dialog for user info
+    DIALOG_STORAGE_USER_INFO = 567;
+
+    // OPEN: Settings > Add fingerprint > Dialog when user touches fingerprint icon.
+    DIALOG_FINGERPRINT_ICON_TOUCH = 568;
+
+    // OPEN: Settings > Add fingerprint > Error dialog
+    DIALOG_FINGERPINT_ERROR = 569;
+
+    // OPEN: Settings > Fingerprint > Rename or delete dialog
+    DIALOG_FINGERPINT_EDIT = 570;
+
+    // OPEN: Settings > Fingerprint > Dialog for deleting last fingerprint
+    DIALOG_FINGERPINT_DELETE_LAST = 571;
+
+    // OPEN: SUW > Fingerprint > Dialog to confirm cancel fingerprint setup.
+    DIALOG_FINGERPRINT_CANCEL_SETUP = 572;
+
+    // OPEN: SUW > Fingerprint > Dialog to confirm skip fingerprint setup entirely.
+    DIALOG_FINGERPRINT_SKIP_SETUP = 573;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 562d950..582b19b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -341,6 +341,8 @@
         mDoubleTapDetected = false;
         mSecondFingerDoubleTap = false;
         mGestureStarted = false;
+        mGestureDetector.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_CANCEL,
+                0.0f, 0.0f, 0));
         cancelGesture();
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 32adc20..0b83e66 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1315,8 +1315,14 @@
     private void updateServicesLocked(UserState userState) {
         Map<ComponentName, Service> componentNameToServiceMap =
                 userState.mComponentNameToServiceMap;
-        boolean isUnlockingOrUnlocked = mContext.getSystemService(UserManager.class)
-                .isUserUnlockingOrUnlocked(userState.mUserId);
+        boolean isUnlockingOrUnlocked;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            isUnlockingOrUnlocked = mContext.getSystemService(UserManager.class)
+                    .isUserUnlockingOrUnlocked(userState.mUserId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
 
         for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
             AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
@@ -2531,7 +2537,7 @@
             // share the accessibility state of the parent. The call below
             // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
-                    .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.USER_CURRENT);
+                    .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.getCallingUserId());
             return resolvedUserId == mCurrentUserId;
         }
 
@@ -3733,13 +3739,6 @@
                 Rect boundsInScreen = mTempRect;
                 focus.getBoundsInScreen(boundsInScreen);
 
-                // Clip to the window bounds.
-                Rect windowBounds = mTempRect1;
-                getWindowBounds(focus.getWindowId(), windowBounds);
-                if (!boundsInScreen.intersect(windowBounds)) {
-                    return false;
-                }
-
                 // Apply magnification if needed.
                 MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
                 if (spec != null && !spec.isNop()) {
@@ -3747,6 +3746,13 @@
                     boundsInScreen.scale(1 / spec.scale);
                 }
 
+                // Clip to the window bounds.
+                Rect windowBounds = mTempRect1;
+                getWindowBounds(focus.getWindowId(), windowBounds);
+                if (!boundsInScreen.intersect(windowBounds)) {
+                    return false;
+                }
+
                 // Clip to the screen bounds.
                 Point screenSize = mTempPoint;
                 mDefaultDisplay.getRealSize(screenSize);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index 1532946..c81a876 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.os.Binder;
 import android.provider.Settings.Secure;
 import android.view.accessibility.AccessibilityManager;
 
@@ -60,10 +61,15 @@
         final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
 
         int daltonizerMode = AccessibilityManager.DALTONIZER_DISABLED;
-        if (Secure.getIntForUser(cr,
-                Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
-            daltonizerMode = Secure.getIntForUser(cr,
-                    Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, userId);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (Secure.getIntForUser(cr,
+                    Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
+                daltonizerMode = Secure.getIntForUser(cr,
+                        Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
 
         float[] grayscaleMatrix = null;
@@ -83,9 +89,14 @@
         final ContentResolver cr = context.getContentResolver();
         final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
 
-        final boolean invertColors = Secure.getIntForUser(cr,
-                Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0;
-        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
-                invertColors ? MATRIX_INVERT_COLOR : null);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            final boolean invertColors = Secure.getIntForUser(cr,
+                    Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0;
+            dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
+                    invertColors ? MATRIX_INVERT_COLOR : null);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a789157..d1a1bd4 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -302,7 +302,7 @@
      */
     private final boolean isBluetoothPersistedStateOn() {
         return Settings.Global.getInt(mContentResolver,
-                Settings.Global.BLUETOOTH_ON, 0) != BLUETOOTH_OFF;
+                Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) != BLUETOOTH_OFF;
     }
 
     /**
@@ -310,7 +310,7 @@
      */
     private final boolean isBluetoothPersistedStateOnBluetooth() {
         return Settings.Global.getInt(mContentResolver,
-                Settings.Global.BLUETOOTH_ON, 0) == BLUETOOTH_ON_BLUETOOTH;
+                Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
     }
 
     /**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b08549f..667a4a9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -24,6 +24,7 @@
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -262,6 +263,11 @@
         DONT_REAP
     };
 
+    private enum UnneededFor {
+        LINGER,    // Determine whether this network is unneeded and should be lingered.
+        TEARDOWN,  // Determine whether this network is unneeded and should be torn down.
+    }
+
     /**
      * used internally to change our mobile data enabled flag
      */
@@ -392,6 +398,11 @@
     private static final int EVENT_REQUEST_LINKPROPERTIES  = 32;
     private static final int EVENT_REQUEST_NETCAPABILITIES = 33;
 
+    /**
+     * Used internally to (re)configure avoid bad wifi setting.
+     */
+    private static final int EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI = 34;
+
     /** Handler thread used for both of the handlers below. */
     @VisibleForTesting
     protected final HandlerThread mHandlerThread;
@@ -691,13 +702,13 @@
         if (DBG) log("ConnectivityService starting up");
 
         mMetricsLog = logger;
-        mDefaultRequest = createInternetRequestForTransport(-1);
+        mDefaultRequest = createInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
         NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
         mNetworkRequests.put(mDefaultRequest, defaultNRI);
         mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
 
         mDefaultMobileDataRequest = createInternetRequestForTransport(
-                NetworkCapabilities.TRANSPORT_CELLULAR);
+                NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
 
         mHandlerThread = createHandlerThread();
         mHandlerThread.start();
@@ -846,17 +857,26 @@
                 Settings.Global.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS,
                 LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS);
         mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
+
+        intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                mHandler.sendEmptyMessage(EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI);
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+        updateAvoidBadWifi();
     }
 
-    private NetworkRequest createInternetRequestForTransport(int transportType) {
+    private NetworkRequest createInternetRequestForTransport(
+            int transportType, NetworkRequest.Type type) {
         NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
         netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         if (transportType > -1) {
             netCap.addTransportType(transportType);
         }
-        return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(),
-                NetworkRequest.Type.REQUEST);
+        return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
     }
 
     // Used only for testing.
@@ -896,6 +916,12 @@
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
                 EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+
+        // Watch for whether to automatically switch away from wifi networks that lose Internet
+        // access.
+        mSettingsObserver.observe(
+                Settings.Global.getUriFor(Settings.Global.NETWORK_AVOID_BAD_WIFI),
+                EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI);
     }
 
     private synchronized int nextNetworkRequestId() {
@@ -1964,8 +1990,12 @@
         for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
             pw.println(nai.toString());
             pw.increaseIndent();
-            pw.println(String.format("Requests: %d request/%d total",
-                    nai.numRequestNetworkRequests(), nai.numNetworkRequests()));
+            pw.println(String.format(
+                    "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d",
+                    nai.numForegroundNetworkRequests(),
+                    nai.numNetworkRequests() - nai.numRequestNetworkRequests(),
+                    nai.numBackgroundNetworkRequests(),
+                    nai.numNetworkRequests()));
             pw.increaseIndent();
             for (int i = 0; i < nai.numNetworkRequests(); i++) {
                 pw.println(nai.requestAt(i).toString());
@@ -2124,15 +2154,11 @@
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
                     final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
                     if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
-                            networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                            networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) ||
+                            networkCapabilities.hasCapability(NET_CAPABILITY_FOREGROUND)) {
                         Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
-                    if (nai.everConnected && !nai.networkCapabilities.equalImmutableCapabilities(
-                            networkCapabilities)) {
-                        Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
-                                + nai.networkCapabilities + " -> " + networkCapabilities);
-                    }
-                    updateCapabilities(nai, networkCapabilities);
+                    updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
@@ -2203,13 +2229,14 @@
                     if (nai != null) {
                         final boolean valid =
                                 (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+                        final boolean wasValidated = nai.lastValidated;
                         if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
                                 (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
                         if (valid != nai.lastValidated) {
                             final int oldScore = nai.getCurrentScore();
                             nai.lastValidated = valid;
                             nai.everValidated |= valid;
-                            updateCapabilities(nai, nai.networkCapabilities);
+                            updateCapabilities(oldScore, nai, nai.networkCapabilities);
                             // If score has changed, rebroadcast to NetworkFactories. b/17726566
                             if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
                         }
@@ -2221,6 +2248,9 @@
                                 NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                                 (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
                                 0, redirectUrlBundle);
+                         if (wasValidated && !nai.lastValidated) {
+                             handleNetworkUnvalidated(nai);
+                         }
                     }
                     break;
                 }
@@ -2233,9 +2263,10 @@
                     }
                     // If captive portal status has changed, update capabilities.
                     if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
+                        final int oldScore = nai.getCurrentScore();
                         nai.lastCaptivePortalDetected = visible;
                         nai.everCaptivePortalDetected |= visible;
-                        updateCapabilities(nai, nai.networkCapabilities);
+                        updateCapabilities(oldScore, nai, nai.networkCapabilities);
                     }
                     if (!visible) {
                         mNotifier.clearNotification(netId);
@@ -2286,15 +2317,13 @@
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
         //    one lingered request, start lingering.
         nai.updateLingerTimer();
-        if (nai.isLingering() && nai.numRequestNetworkRequests() > 0) {
+        if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
             if (DBG) log("Unlingering " + nai.name());
             nai.unlinger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
-        } else if (unneeded(nai) && nai.getLingerExpiry() > 0) {  // unneeded() calls isLingering()
+        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
             int lingerTime = (int) (nai.getLingerExpiry() - now);
-            if (DBG) {
-                Log.d(TAG, "Lingering " + nai.name() + " for " + lingerTime + "ms");
-            }
+            if (DBG) log("Lingering " + nai.name() + " for " + lingerTime + "ms");
             nai.linger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
@@ -2473,15 +2502,37 @@
         }
     }
 
-    // Is nai unneeded by all NetworkRequests (and should be disconnected)?
-    // This is whether it is satisfying any NetworkRequests or were it to become validated,
-    // would it have a chance of satisfying any NetworkRequests.
-    private boolean unneeded(NetworkAgentInfo nai) {
-        if (!nai.everConnected || nai.isVPN() ||
-               nai.isLingering() || nai.numRequestNetworkRequests() > 0) {
+    // Determines whether the network is the best (or could become the best, if it validated), for
+    // none of a particular type of NetworkRequests. The type of NetworkRequests considered depends
+    // on the value of reason:
+    //
+    // - UnneededFor.TEARDOWN: non-listen NetworkRequests. If a network is unneeded for this reason,
+    //   then it should be torn down.
+    // - UnneededFor.LINGER: foreground NetworkRequests. If a network is unneeded for this reason,
+    //   then it should be lingered.
+    private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
+        final int numRequests;
+        switch (reason) {
+            case TEARDOWN:
+                numRequests = nai.numRequestNetworkRequests();
+                break;
+            case LINGER:
+                numRequests = nai.numForegroundNetworkRequests();
+                break;
+            default:
+                Slog.wtf(TAG, "Invalid reason. Cannot happen.");
+                return true;
+        }
+
+        if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
             return false;
         }
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) {
+                // Background requests don't affect lingering.
+                continue;
+            }
+
             // If this Network is already the highest scoring Network for a request, or if
             // there is hope for it to become one if it validated, then it is needed.
             if (nri.request.isRequest() && nai.satisfies(nri.request) &&
@@ -2583,6 +2634,7 @@
             boolean wasKept = false;
             NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
             if (nai != null) {
+                boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
                 if (VDBG) {
                     log(" Removing from current network " + nai.name() +
@@ -2591,13 +2643,17 @@
                 // If there are still lingered requests on this network, don't tear it down,
                 // but resume lingering instead.
                 updateLingerState(nai, SystemClock.elapsedRealtime());
-                if (unneeded(nai)) {
+                if (unneeded(nai, UnneededFor.TEARDOWN)) {
                     if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
                     teardownUnneededNetwork(nai);
                 } else {
                     wasKept = true;
                 }
                 mNetworkForRequestId.remove(nri.request.requestId);
+                if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
+                    // Went from foreground to background.
+                    updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+                }
             }
 
             // TODO: remove this code once we know that the Slog.wtf is never hit.
@@ -2711,6 +2767,56 @@
                 PROMPT_UNVALIDATED_DELAY_MS);
     }
 
+    private boolean mAvoidBadWifi;
+
+    public boolean avoidBadWifi() {
+        return mAvoidBadWifi;
+    }
+
+    @VisibleForTesting
+    public boolean updateAvoidBadWifi() {
+        // There are two modes: either we always automatically avoid unvalidated wifi, or we show a
+        // dialog and don't switch to it. The behaviour is controlled by the NETWORK_AVOID_BAD_WIFI
+        // setting. If the setting has no value, then the value is taken from the config value,
+        // which can be changed via OEM/carrier overlays.
+        //
+        // The only valid values for NETWORK_AVOID_BAD_WIFI are null and unset. Currently, the unit
+        // test uses 0 in order to avoid having to mock out fetching the carrier setting.
+        int defaultAvoidBadWifi =
+            mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi);
+        int avoid = Settings.Global.getInt(mContext.getContentResolver(),
+            Settings.Global.NETWORK_AVOID_BAD_WIFI, defaultAvoidBadWifi);
+
+        boolean prev = mAvoidBadWifi;
+        mAvoidBadWifi = (avoid == 1);
+        return mAvoidBadWifi != prev;
+    }
+
+    private void showValidationNotification(NetworkAgentInfo nai, NotificationType type) {
+        final String action;
+        switch (type) {
+            case NO_INTERNET:
+                action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
+                break;
+            case LOST_INTERNET:
+                action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
+                break;
+            default:
+                Slog.wtf(TAG, "Unknown notification type " + type);
+                return;
+        }
+
+        Intent intent = new Intent(action);
+        intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setClassName("com.android.settings",
+                "com.android.settings.wifi.WifiNoInternetDialog");
+
+        PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
+                mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+        mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, true);
+    }
+
     private void handlePromptUnvalidated(Network network) {
         if (VDBG) log("handlePromptUnvalidated " + network);
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
@@ -2722,18 +2828,22 @@
                 !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
             return;
         }
+        showValidationNotification(nai, NotificationType.NO_INTERNET);
+    }
 
-        Intent intent = new Intent(ConnectivityManager.ACTION_PROMPT_UNVALIDATED);
-        intent.setData(Uri.fromParts("netId", Integer.toString(network.netId), null));
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.setClassName("com.android.settings",
-                "com.android.settings.wifi.WifiNoInternetDialog");
+    // TODO: Delete this like updateMobileDataAlwaysOn above.
+    @VisibleForTesting
+    void updateNetworkAvoidBadWifi() {
+        mHandler.sendEmptyMessage(EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI);
+    }
 
-        PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
-                mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+    private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
+        NetworkCapabilities nc = nai.networkCapabilities;
+        if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
 
-        mNotifier.showNotification(nai.network.netId, NotificationType.NO_INTERNET, nai, null,
-                pendingIntent, true);
+        if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && !avoidBadWifi()) {
+            showValidationNotification(nai, NotificationType.LOST_INTERNET);
+        }
     }
 
     private class InternalHandler extends Handler {
@@ -2822,6 +2932,12 @@
                     handleMobileDataAlwaysOn();
                     break;
                 }
+                case EVENT_CONFIGURE_NETWORK_AVOID_BAD_WIFI: {
+                    if (updateAvoidBadWifi()) {
+                        rematchAllNetworksAndRequests(null, 0);
+                    }
+                    break;
+                }
                 case EVENT_REQUEST_LINKPROPERTIES:
                     handleRequestLinkProperties((NetworkRequest) msg.obj, msg.arg1);
                     break;
@@ -4472,10 +4588,19 @@
      * augmented with any stateful capabilities implied from {@code networkAgent}
      * (e.g., validated status and captive portal status).
      *
+     * @param oldScore score of the network before any of the changes that prompted us
+     *                 to call this function.
      * @param nai the network having its capabilities updated.
      * @param networkCapabilities the new network capabilities.
      */
-    private void updateCapabilities(NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
+    private void updateCapabilities(
+            int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
+        if (nai.everConnected && !nai.networkCapabilities.equalImmutableCapabilities(
+                networkCapabilities)) {
+            Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
+                    + nai.networkCapabilities + " -> " + networkCapabilities);
+        }
+
         // Don't modify caller's NetworkCapabilities.
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         if (nai.lastValidated) {
@@ -4488,21 +4613,39 @@
         } else {
             networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
         }
-        if (!Objects.equals(nai.networkCapabilities, networkCapabilities)) {
-            final int oldScore = nai.getCurrentScore();
-            if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) !=
-                    networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
-                try {
-                    mNetd.setNetworkPermission(nai.network.netId,
-                            networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) ?
-                                    null : NetworkManagementService.PERMISSION_SYSTEM);
-                } catch (RemoteException e) {
-                    loge("Exception in setNetworkPermission: " + e);
-                }
+        if (nai.isBackgroundNetwork()) {
+            networkCapabilities.removeCapability(NET_CAPABILITY_FOREGROUND);
+        } else {
+            networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
+        }
+
+        if (Objects.equals(nai.networkCapabilities, networkCapabilities)) return;
+
+        if (nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) !=
+                networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+            try {
+                mNetd.setNetworkPermission(nai.network.netId,
+                        networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) ?
+                                null : NetworkManagementService.PERMISSION_SYSTEM);
+            } catch (RemoteException e) {
+                loge("Exception in setNetworkPermission: " + e);
             }
-            synchronized (nai) {
-                nai.networkCapabilities = networkCapabilities;
-            }
+        }
+
+        final NetworkCapabilities prevNc = nai.networkCapabilities;
+        synchronized (nai) {
+            nai.networkCapabilities = networkCapabilities;
+        }
+        if (nai.getCurrentScore() == oldScore &&
+                networkCapabilities.equalRequestableCapabilities(prevNc)) {
+            // If the requestable capabilities haven't changed, and the score hasn't changed, then
+            // the change we're processing can't affect any requests, it can only affect the listens
+            // on this network. We might have been called by rematchNetworkAndRequests when a
+            // network changed foreground state.
+            processListenRequests(nai, true);
+        } else {
+            // If the requestable capabilities have changed or the score changed, we can't have been
+            // called by rematchNetworkAndRequests, so it's safe to start a rematch.
             rematchAllNetworksAndRequests(nai, oldScore);
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
@@ -4625,8 +4768,13 @@
         // must be no other active linger timers, and we must stop lingering.
         oldNetwork.clearLingerState();
 
-        if (unneeded(oldNetwork)) {
+        if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
+            // Tear the network down.
             teardownUnneededNetwork(oldNetwork);
+        } else {
+            // Put the network in the background.
+            updateCapabilities(oldNetwork.getCurrentScore(), oldNetwork,
+                    oldNetwork.networkCapabilities);
         }
     }
 
@@ -4644,6 +4792,31 @@
         setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
     }
 
+    private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
+        // For consistency with previous behaviour, send onLost callbacks before onAvailable.
+        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            NetworkRequest nr = nri.request;
+            if (!nr.isListen()) continue;
+            if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
+                nai.removeRequest(nri.request.requestId);
+                callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
+            }
+        }
+
+        if (capabilitiesChanged) {
+            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+        }
+
+        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            NetworkRequest nr = nri.request;
+            if (!nr.isListen()) continue;
+            if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
+                nai.addRequest(nr);
+                notifyNetworkCallback(nai, nri);
+            }
+        }
+    }
+
     // Handles a network appearing or improving its score.
     //
     // - Evaluates all current NetworkRequests that can be
@@ -4677,13 +4850,25 @@
         boolean keep = newNetwork.isVPN();
         boolean isNewDefault = false;
         NetworkAgentInfo oldDefaultNetwork = null;
+
+        final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork();
+        final int score = newNetwork.getCurrentScore();
+
         if (VDBG) log("rematching " + newNetwork.name());
+
         // Find and migrate to this Network any NetworkRequests for
         // which this network is now the best.
         ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
         ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>();
-        if (VDBG) log(" network has: " + newNetwork.networkCapabilities);
+        NetworkCapabilities nc = newNetwork.networkCapabilities;
+        if (VDBG) log(" network has: " + nc);
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            // Process requests in the first pass and listens in the second pass. This allows us to
+            // change a network's capabilities depending on which requests it has. This is only
+            // correct if the change in capabilities doesn't affect whether the network satisfies
+            // requests or not, and doesn't affect the network's score.
+            if (nri.request.isListen()) continue;
+
             final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
             final boolean satisfies = newNetwork.satisfies(nri.request);
             if (newNetwork == currentNetwork && satisfies) {
@@ -4698,22 +4883,14 @@
             // check if it satisfies the NetworkCapabilities
             if (VDBG) log("  checking if request is satisfied: " + nri.request);
             if (satisfies) {
-                if (nri.request.isListen()) {
-                    // This is not a request, it's a callback listener.
-                    // Add it to newNetwork regardless of score.
-                    if (newNetwork.addRequest(nri.request)) addedRequests.add(nri);
-                    continue;
-                }
-
                 // next check if it's better than any current network we're using for
                 // this request
                 if (VDBG) {
                     log("currentScore = " +
                             (currentNetwork != null ? currentNetwork.getCurrentScore() : 0) +
-                            ", newScore = " + newNetwork.getCurrentScore());
+                            ", newScore = " + score);
                 }
-                if (currentNetwork == null ||
-                        currentNetwork.getCurrentScore() < newNetwork.getCurrentScore()) {
+                if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
                     if (VDBG) log("rematch for " + newNetwork.name());
                     if (currentNetwork != null) {
                         if (VDBG) log("   accepting network in place of " + currentNetwork.name());
@@ -4735,7 +4912,7 @@
                     // TODO - this could get expensive if we have alot of requests for this
                     // network.  Think about if there is a way to reduce this.  Push
                     // netid->request mapping to each factory?
-                    sendUpdatedScoreToFactories(nri.request, newNetwork.getCurrentScore());
+                    sendUpdatedScoreToFactories(nri.request, score);
                     if (isDefaultRequest(nri)) {
                         isNewDefault = true;
                         oldDefaultNetwork = currentNetwork;
@@ -4747,7 +4924,7 @@
             } else if (newNetwork.isSatisfyingRequest(nri.request.requestId)) {
                 // If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
                 // mark it as no longer satisfying "nri".  Because networks are processed by
-                // rematchAllNetworkAndRequests() in descending score order, "currentNetwork" will
+                // rematchAllNetworksAndRequests() in descending score order, "currentNetwork" will
                 // match "newNetwork" before this loop will encounter a "currentNetwork" with higher
                 // score than "newNetwork" and where "currentNetwork" no longer satisfies "nri".
                 // This means this code doesn't have to handle the case where "currentNetwork" no
@@ -4761,16 +4938,14 @@
                     mNetworkForRequestId.remove(nri.request.requestId);
                     sendUpdatedScoreToFactories(nri.request, 0);
                 } else {
-                    if (nri.request.isRequest()) {
-                        Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
-                                newNetwork.name() +
-                                " without updating mNetworkForRequestId or factories!");
-                    }
+                    Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
+                            newNetwork.name() +
+                            " without updating mNetworkForRequestId or factories!");
                 }
-                // TODO: technically, sending CALLBACK_LOST here is
-                // incorrect if nri is a request (not a listen) and there
-                // is a replacement network currently connected that can
-                // satisfy it. However, the only capability that can both
+                // TODO: Technically, sending CALLBACK_LOST here is
+                // incorrect if there is a replacement network currently
+                // connected that can satisfy nri, which is a request
+                // (not a listen). However, the only capability that can both
                 // a) be requested and b) change is NET_CAPABILITY_TRUSTED,
                 // so this code is only incorrect for a network that loses
                 // the TRUSTED capability, which is a rare case.
@@ -4795,6 +4970,28 @@
             }
         }
 
+        if (!newNetwork.networkCapabilities.equalRequestableCapabilities(nc)) {
+            Slog.wtf(TAG, String.format(
+                    "BUG: %s changed requestable capabilities during rematch: %s -> %s",
+                    nc, newNetwork.networkCapabilities));
+        }
+        if (newNetwork.getCurrentScore() != score) {
+            Slog.wtf(TAG, String.format(
+                    "BUG: %s changed score during rematch: %d -> %d",
+                    score, newNetwork.getCurrentScore()));
+        }
+
+        // Second pass: process all listens.
+        if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
+            // If the network went from background to foreground or vice versa, we need to update
+            // its foreground state. It is safe to do this after rematching the requests because
+            // NET_CAPABILITY_FOREGROUND does not affect requests, as is not a requestable
+            // capability and does not affect the network's score (see the Slog.wtf call above).
+            updateCapabilities(score, newNetwork, newNetwork.networkCapabilities);
+        } else {
+            processListenRequests(newNetwork, false);
+        }
+
         // do this after the default net is switched, but
         // before LegacyTypeTracker sends legacy broadcasts
         for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
@@ -4872,7 +5069,7 @@
         }
         if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
             for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-                if (unneeded(nai)) {
+                if (unneeded(nai, UnneededFor.TEARDOWN)) {
                     if (nai.getLingerExpiry() > 0) {
                         // This network has active linger timers and no requests, but is not
                         // lingering. Linger it.
@@ -4986,6 +5183,10 @@
         if (!networkAgent.created
                 && (state == NetworkInfo.State.CONNECTED
                 || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
+
+            // A network that has just connected has zero requests and is thus a foreground network.
+            networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
+
             try {
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
@@ -5147,6 +5348,8 @@
             NetworkRequest nr = networkAgent.requestAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
             if (VDBG) log(" sending notification for " + nr);
+            // TODO: if we're in the middle of a rematch, can we send a CAP_CHANGED callback for
+            // a network that no longer satisfies the listen?
             if (nri.mPendingIntent == null) {
                 callCallbackForRequest(nri, networkAgent, notifyType, arg1);
             } else {
@@ -5281,6 +5484,9 @@
                 }
             }
         }
+
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 71ac544..df1b6f5 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -822,7 +822,7 @@
         public void onUnlockUser(final @UserIdInt int userHandle) {
             // Called on ActivityManager thread.
             mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
-                    userHandle));
+                    userHandle /* arg1 */, 0 /* arg2 */));
         }
     }
 
@@ -3077,8 +3077,8 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Found an input method " + p);
                 }
-            } catch (XmlPullParserException | IOException e) {
-                Slog.w(TAG, "Unable to load input method " + compName, e);
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Unable to load input method " + compName, e);
             }
         }
 
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 3fdcceb..83d374c 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -364,6 +364,7 @@
             buildResolveList(intent, categories, debug, defaultOnly,
                     resolvedType, scheme, listCut.get(i), resultList, userId);
         }
+        filterResults(resultList);
         sortResults(resultList);
         return resultList;
     }
@@ -457,6 +458,7 @@
             buildResolveList(intent, categories, debug, defaultOnly,
                     resolvedType, scheme, schemeCut, finalList, userId);
         }
+        filterResults(finalList);
         sortResults(finalList);
 
         if (debug) {
@@ -521,6 +523,12 @@
         Collections.sort(results, mResolvePrioritySorter);
     }
 
+    /**
+     * Apply filtering to the results. This happens before the results are sorted.
+     */
+    protected void filterResults(List<R> results) {
+    }
+
     protected void dumpFilter(PrintWriter out, String prefix, F filter) {
         out.print(prefix); out.println(filter);
     }
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 383e25a..2ff036b 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -141,8 +141,12 @@
      * <p>
      * Note that if there are no matching encryption-aware services, we may not
      * bind to a real service until after the current user is unlocked.
+     *
+     * @returns {@code true} if a potential service implementation was found.
      */
     public boolean start() {
+        if (isServiceMissing()) return false;
+
         synchronized (mLock) {
             bindBestPackageLocked(mServicePackageName, false);
         }
@@ -174,6 +178,17 @@
     }
 
     /**
+     * Check if any instance of this service is present on the device,
+     * regardless of it being encryption-aware or not.
+     */
+    private boolean isServiceMissing() {
+        final Intent intent = new Intent(mAction);
+        final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+        return mPm.queryIntentServicesAsUser(intent, flags, mCurrentUserId).isEmpty();
+    }
+
+    /**
      * Searches and binds to the best package, or do nothing if the best package
      * is already bound, unless force rebinding is requested.
      *
@@ -181,7 +196,7 @@
      *            packages if it is {@code null}.
      * @param forceRebind Force a rebinding to the best package if it's already
      *            bound.
-     * @return {@code true} if a valid package was found to bind to.
+     * @returns {@code true} if a valid package was found to bind to.
      */
     private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
         Intent intent = new Intent(mAction);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 48e9ac7..b152372 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -928,14 +928,16 @@
         String interestedPackages = null;
         try {
             String[] allPackages = mPackageManager.getPackagesForUid(uid);
-            for(String aPackage : allPackages) {
-                ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
-                        PackageManager.GET_META_DATA);
-                Bundle b = ai.metaData;
-                if(b == null) {
-                    return;
+            if (allPackages != null) {
+                for(String aPackage : allPackages) {
+                    ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
+                            PackageManager.GET_META_DATA);
+                    Bundle b = ai.metaData;
+                    if(b == null) {
+                        return;
+                    }
+                    interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
                 }
-                interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
             }
         } catch (PackageManager.NameNotFoundException e) {
             Log.d("NameNotFoundException", e.getMessage());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ba4f041..0c85329 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -287,7 +287,6 @@
 import static android.provider.Settings.Global.LENIENT_BACKGROUND_CHECK;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.provider.Settings.System.FONT_SCALE;
-import static android.security.KeyChain.ACTION_TRUST_STORE_CHANGED;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -556,7 +555,7 @@
     public IntentFirewall mIntentFirewall;
 
     // Whether we should show our dialogs (ANR, crash, etc) or just perform their
-    // default actuion automatically.  Important for devices without direct input
+    // default action automatically.  Important for devices without direct input
     // devices.
     private boolean mShowDialogs = true;
     private boolean mInVrMode = false;
@@ -628,7 +627,8 @@
     final AppErrors mAppErrors;
 
     public boolean canShowErrorDialogs() {
-        return mShowDialogs && !mSleeping && !mShuttingDown;
+        return mShowDialogs && !mSleeping && !mShuttingDown
+                && mLockScreenShown != LOCK_SCREEN_SHOWN;
     }
 
     private static final class PriorityState {
@@ -1121,19 +1121,21 @@
     final AppOpsService mAppOpsService;
 
     /**
-     * Current configuration information.  HistoryRecord objects are given
-     * a reference to this object to indicate which configuration they are
-     * currently running in, so this object must be kept immutable.
+     * Current global configuration information. Contains general settings for the entire system,
+     * also corresponds to the merged configuration of the default display.
      */
-    Configuration mConfiguration = new Configuration();
+    Configuration mGlobalConfiguration = new Configuration();
+
+    /** Current sequencing integer of the configuration, for skipping old configurations. */
+    private int mConfigurationSeq;
 
     /**
-     * Current sequencing integer of the configuration, for skipping old
-     * configurations.
+     * Temp object used when global configuration is updated. It is also sent to outer world
+     * instead of {@link #mGlobalConfiguration} because we don't trust anyone...
      */
-    int mConfigurationSeq = 0;
+    private Configuration mTempGlobalConfig = new Configuration();
 
-    boolean mSuppressResizeConfigChanges = false;
+    boolean mSuppressResizeConfigChanges;
 
     /**
      * Hardware-reported OpenGLES version.
@@ -1717,6 +1719,7 @@
                     }
                     if (ar != null && mCompatModePackages.getPackageNotifyUnsupportedZoomLocked(
                             ar.packageName)) {
+                        // TODO(multi-display): Show dialog on appropriate display.
                         mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog(
                                 ActivityManagerService.this, mContext, ar.info.applicationInfo);
                         mUnsupportedDisplaySizeDialog.show();
@@ -2292,6 +2295,9 @@
             } break;
             case VR_MODE_CHANGE_MSG: {
                 VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+                if (vrService == null) {
+                    break;
+                }
                 final ActivityRecord r = (ActivityRecord) msg.obj;
                 boolean vrMode;
                 ComponentName requestedPackage;
@@ -2304,7 +2310,7 @@
                     callingPackage = r.info.getComponentName();
                     if (mInVrMode != vrMode) {
                         mInVrMode = vrMode;
-                        mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
+                        mShowDialogs = shouldShowDialogs(mGlobalConfiguration, mInVrMode);
                         if (r.app != null) {
                             ProcessRecord proc = r.app;
                             if (proc.vrThreadTid > 0) {
@@ -2680,10 +2686,10 @@
 
         mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
 
-        mConfiguration.setToDefaults();
-        mConfiguration.setLocales(LocaleList.getDefault());
+        mGlobalConfiguration.setToDefaults();
+        mGlobalConfiguration.setLocales(LocaleList.getDefault());
 
-        mConfigurationSeq = mConfiguration.seq = 1;
+        mConfigurationSeq = mGlobalConfiguration.seq = 1;
         mProcessCpuTracker.init();
 
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
@@ -3120,8 +3126,8 @@
     }
 
     final void showUnsupportedZoomDialogIfNeededLocked(ActivityRecord r) {
-        if (mConfiguration.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
-                && r.appInfo.requiresSmallestWidthDp > mConfiguration.smallestScreenWidthDp) {
+        if (mGlobalConfiguration.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
+                && r.appInfo.requiresSmallestWidthDp > mGlobalConfiguration.smallestScreenWidthDp) {
             final Message msg = Message.obtain();
             msg.what = SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG;
             msg.obj = r;
@@ -3780,16 +3786,19 @@
             app.killed = false;
             app.killedByAm = false;
             checkTime(startTime, "startProcess: starting to update pids map");
+            ProcessRecord oldApp;
             synchronized (mPidsSelfLocked) {
-                ProcessRecord oldApp;
-                // If there is already an app occupying that pid that hasn't been cleaned up
-                if ((oldApp = mPidsSelfLocked.get(startResult.pid)) != null && !app.isolated) {
-                    // Clean up anything relating to this pid first
-                    Slog.w(TAG, "Reusing pid " + startResult.pid
-                            + " while app is still mapped to it");
-                    cleanUpApplicationRecordLocked(oldApp, false, false, -1,
-                            true /*replacingPid*/);
-                }
+                oldApp = mPidsSelfLocked.get(startResult.pid);
+            }
+            // If there is already an app occupying that pid that hasn't been cleaned up
+            if (oldApp != null && !app.isolated) {
+                // Clean up anything relating to this pid first
+                Slog.w(TAG, "Reusing pid " + startResult.pid
+                        + " while app is still mapped to it");
+                cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+                        true /*replacingPid*/);
+            }
+            synchronized (mPidsSelfLocked) {
                 this.mPidsSelfLocked.put(startResult.pid, app);
                 if (isActivityProcess) {
                     Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
@@ -4725,7 +4734,7 @@
             final long origId = Binder.clearCallingIdentity();
             mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
             Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                    mConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
+                    mGlobalConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
             if (config != null) {
                 r.frozenBeforeDestroy = true;
                 if (!updateConfigurationLocked(config, r, false)) {
@@ -6508,7 +6517,7 @@
                                  PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
             }
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
-                    + processName + " with config " + mConfiguration);
+                    + processName + " with config " + mGlobalConfiguration);
             ApplicationInfo appInfo = app.instrumentationInfo != null
                     ? app.instrumentationInfo : app.info;
             app.compat = compatibilityInfoForPackageLocked(appInfo);
@@ -6533,7 +6542,7 @@
                     app.instrumentationUiAutomationConnection, testMode,
                     mBinderTransactionTrackingEnabled, enableTrackAllocation,
                     isRestrictedBackupMode || !normalMode, app.persistent,
-                    new Configuration(mConfiguration), app.compat,
+                    new Configuration(mGlobalConfiguration), app.compat,
                     getCommonServicesLocked(app.isolated),
                     mCoreSettingsObserver.getCoreSettingsLocked(),
                     buildSerial);
@@ -9295,7 +9304,7 @@
                 r.task.stack.getDisplaySize(displaySize);
                 thumbnailInfo.taskWidth = displaySize.x;
                 thumbnailInfo.taskHeight = displaySize.y;
-                thumbnailInfo.screenOrientation = mConfiguration.orientation;
+                thumbnailInfo.screenOrientation = mGlobalConfiguration.orientation;
 
                 TaskRecord task = new TaskRecord(this,
                         mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
@@ -13127,11 +13136,11 @@
                 mSupportsFreeformWindowManagement = false;
                 mSupportsPictureInPicture = false;
             }
-            // This happens before any activities are started, so we can
-            // change mConfiguration in-place.
+            // This happens before any activities are started, so we can change global configuration
+            // in-place.
             updateConfigurationLocked(configuration, null, true);
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Initial config: " + mConfiguration);
+                    "Initial config: " + mGlobalConfiguration);
 
             // Load resources only after the current configuration has been set.
             final Resources res = mContext.getResources();
@@ -13146,10 +13155,11 @@
                     com.android.internal.R.string.config_appsNotReportingCrashes));
             mUserController.mUserSwitchUiEnabled = !res.getBoolean(
                     com.android.internal.R.bool.config_customUserSwitchUi);
-            if ((mConfiguration.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+            if ((mGlobalConfiguration.uiMode & UI_MODE_TYPE_TELEVISION)
+                    == UI_MODE_TYPE_TELEVISION) {
                 mFullscreenThumbnailScale = (float) res
                     .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
-                    (float) mConfiguration.screenWidthDp;
+                    (float) mGlobalConfiguration.screenWidthDp;
             } else {
                 mFullscreenThumbnailScale = res.getFraction(
                     com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
@@ -14742,7 +14752,7 @@
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
         if (dumpPackage == null) {
-            pw.println("  mConfiguration: " + mConfiguration);
+            pw.println("  mGlobalConfiguration: " + mGlobalConfiguration);
         }
         if (dumpAll) {
             pw.println("  mConfigWillChange: " + getFocusedStack().mConfigWillChange);
@@ -18725,15 +18735,15 @@
     public ConfigurationInfo getDeviceConfigurationInfo() {
         ConfigurationInfo config = new ConfigurationInfo();
         synchronized (this) {
-            config.reqTouchScreen = mConfiguration.touchscreen;
-            config.reqKeyboardType = mConfiguration.keyboard;
-            config.reqNavigation = mConfiguration.navigation;
-            if (mConfiguration.navigation == Configuration.NAVIGATION_DPAD
-                    || mConfiguration.navigation == Configuration.NAVIGATION_TRACKBALL) {
+            config.reqTouchScreen = mGlobalConfiguration.touchscreen;
+            config.reqKeyboardType = mGlobalConfiguration.keyboard;
+            config.reqNavigation = mGlobalConfiguration.navigation;
+            if (mGlobalConfiguration.navigation == Configuration.NAVIGATION_DPAD
+                    || mGlobalConfiguration.navigation == Configuration.NAVIGATION_TRACKBALL) {
                 config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
             }
-            if (mConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED
-                    && mConfiguration.keyboard != Configuration.KEYBOARD_NOKEYS) {
+            if (mGlobalConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED
+                    && mGlobalConfiguration.keyboard != Configuration.KEYBOARD_NOKEYS) {
                 config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
             }
             config.reqGlEsVersion = GL_ES_VERSION;
@@ -18757,7 +18767,7 @@
     public Configuration getConfiguration() {
         Configuration ci;
         synchronized(this) {
-            ci = new Configuration(mConfiguration);
+            ci = new Configuration(mGlobalConfiguration);
             ci.userSetLocale = false;
         }
         return ci;
@@ -18790,8 +18800,8 @@
     @Override
     public void updatePersistentConfiguration(Configuration values) {
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
-                "updateConfiguration()");
-        enforceWriteSettingsPermission("updateConfiguration()");
+                "updatePersistentConfiguration()");
+        enforceWriteSettingsPermission("updatePersistentConfiguration()");
         if (values == null) {
             throw new NullPointerException("Configuration must not be null");
         }
@@ -18815,7 +18825,7 @@
     private void updateFontScaleIfNeeded(@UserIdInt int userId) {
         final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
                 FONT_SCALE, 1.0f, userId);
-        if (mConfiguration.fontScale != scaleFactor) {
+        if (mGlobalConfiguration.fontScale != scaleFactor) {
             final Configuration configuration = mWindowManager.computeNewConfiguration();
             configuration.fontScale = scaleFactor;
             synchronized (this) {
@@ -18843,6 +18853,7 @@
         throw new SecurityException(msg);
     }
 
+    @Override
     public void updateConfiguration(Configuration values) {
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                 "updateConfiguration()");
@@ -18867,10 +18878,12 @@
     }
 
     void updateUserConfigurationLocked() {
-        Configuration configuration = new Configuration(mConfiguration);
+        final Configuration configuration = new Configuration(mGlobalConfiguration);
+        final int currentUserId = mUserController.getCurrentUserIdLocked();
         Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
-                mUserController.getCurrentUserIdLocked(), Settings.System.canWrite(mContext));
-        updateConfigurationLocked(configuration, null, false);
+                currentUserId, Settings.System.canWrite(mContext));
+        updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
+                false /* persistent */, currentUserId, false /* deferResume */);
     }
 
     boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
@@ -18901,129 +18914,147 @@
     private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
             boolean initLocale, boolean persistent, int userId, boolean deferResume) {
         int changes = 0;
+        boolean kept = true;
 
         if (mWindowManager != null) {
             mWindowManager.deferSurfaceLayout();
         }
-        if (values != null) {
-            Configuration newConfig = new Configuration(mConfiguration);
-            changes = newConfig.updateFrom(values);
-            if (changes != 0) {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
-                        "Updating configuration to: " + values);
-
-                EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
-
-                if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
-                    final LocaleList locales = values.getLocales();
-                    int bestLocaleIndex = 0;
-                    if (locales.size() > 1) {
-                        if (mSupportedSystemLocales == null) {
-                            mSupportedSystemLocales =
-                                    Resources.getSystem().getAssets().getLocales();
-                        }
-                        bestLocaleIndex = Math.max(0,
-                                locales.getFirstMatchIndex(mSupportedSystemLocales));
-                    }
-                    SystemProperties.set("persist.sys.locale",
-                            locales.get(bestLocaleIndex).toLanguageTag());
-                    LocaleList.setDefault(locales, bestLocaleIndex);
-                    mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
-                            locales.get(bestLocaleIndex)));
-                }
-
-                mConfigurationSeq++;
-                if (mConfigurationSeq <= 0) {
-                    mConfigurationSeq = 1;
-                }
-                newConfig.seq = mConfigurationSeq;
-                mConfiguration = newConfig;
-                Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
-                mUsageStatsService.reportConfigurationChange(newConfig,
-                        mUserController.getCurrentUserIdLocked());
-                //mUsageStatsService.noteStartConfig(newConfig);
-
-                final Configuration configCopy = new Configuration(mConfiguration);
-
-                // TODO: If our config changes, should we auto dismiss any currently
-                // showing dialogs?
-                mShowDialogs = shouldShowDialogs(newConfig, mInVrMode);
-
-                AttributeCache ac = AttributeCache.instance();
-                if (ac != null) {
-                    ac.updateConfiguration(configCopy);
-                }
-
-                // Make sure all resources in our process are updated
-                // right now, so that anyone who is going to retrieve
-                // resource values after we return will be sure to get
-                // the new ones.  This is especially important during
-                // boot, where the first config change needs to guarantee
-                // all resources have that config before following boot
-                // code is executed.
-                mSystemThread.applyConfigurationToResources(configCopy);
-
-                if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
-                    Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
-                    msg.obj = new Configuration(configCopy);
-                    msg.arg1 = userId;
-                    mHandler.sendMessage(msg);
-                }
-
-                final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
-                if (isDensityChange) {
-                    // Reset the unsupported display size dialog.
-                    mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG);
-
-                    killAllBackgroundProcessesExcept(Build.VERSION_CODES.N,
-                            ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
-                }
-
-                for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                    ProcessRecord app = mLruProcesses.get(i);
-                    try {
-                        if (app.thread != null) {
-                            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
-                                    + app.processName + " new config " + mConfiguration);
-                            app.thread.scheduleConfigurationChanged(configCopy);
-                        }
-                    } catch (Exception e) {
-                    }
-                }
-                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                        | Intent.FLAG_RECEIVER_REPLACE_PENDING
-                        | Intent.FLAG_RECEIVER_FOREGROUND);
-                broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
-                        null, AppOpsManager.OP_NONE, null, false, false,
-                        MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
-                if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
-                    intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
-                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                    if (!mProcessesReady) {
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                    }
-                    broadcastIntentLocked(null, null, intent,
-                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
-                }
+        try {
+            if (values != null) {
+                changes = updateGlobalConfiguration(values, initLocale, persistent, userId,
+                        deferResume);
             }
-            // Update the configuration with WM first and check if any of the stacks need to be
-            // resized due to the configuration change. If so, resize the stacks now and do any
-            // relaunches if necessary. This way we don't need to relaunch again below in
-            // ensureActivityConfigurationLocked().
+
+            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+        } finally {
             if (mWindowManager != null) {
-                final int[] resizedStacks = mWindowManager.setNewConfiguration(mConfiguration);
-                if (resizedStacks != null) {
-                    for (int stackId : resizedStacks) {
-                        final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
-                        mStackSupervisor.resizeStackLocked(
-                                stackId, newBounds, null, null, false, false, deferResume);
-                    }
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+        return kept;
+    }
+
+    /** Update default (global) configuration and notify listeners about changes. */
+    private int updateGlobalConfiguration(@NonNull Configuration values, boolean initLocale,
+            boolean persistent, int userId, boolean deferResume) {
+        mTempGlobalConfig.setTo(mGlobalConfiguration);
+        final int changes = mTempGlobalConfig.updateFrom(values);
+        if (changes == 0) {
+            return 0;
+        }
+
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
+                "Updating global configuration to: " + values);
+
+        EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
+
+        if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
+            final LocaleList locales = values.getLocales();
+            int bestLocaleIndex = 0;
+            if (locales.size() > 1) {
+                if (mSupportedSystemLocales == null) {
+                    mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+                }
+                bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
+            }
+            SystemProperties.set("persist.sys.locale",
+                    locales.get(bestLocaleIndex).toLanguageTag());
+            LocaleList.setDefault(locales, bestLocaleIndex);
+            mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
+                    locales.get(bestLocaleIndex)));
+        }
+
+        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+        mTempGlobalConfig.seq = mConfigurationSeq;
+
+        mGlobalConfiguration.setTo(mTempGlobalConfig);
+        Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempGlobalConfig);
+        // TODO(multi-display): Update UsageEvents#Event to include displayId.
+        mUsageStatsService.reportConfigurationChange(mTempGlobalConfig,
+                mUserController.getCurrentUserIdLocked());
+
+        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
+        mShowDialogs = shouldShowDialogs(mTempGlobalConfig, mInVrMode);
+
+        AttributeCache ac = AttributeCache.instance();
+        if (ac != null) {
+            ac.updateConfiguration(mTempGlobalConfig);
+        }
+
+        // Make sure all resources in our process are updated right now, so that anyone who is going
+        // to retrieve resource values after we return will be sure to get the new ones. This is
+        // especially important during boot, where the first config change needs to guarantee all
+        // resources have that config before following boot code is executed.
+        mSystemThread.applyConfigurationToResources(mTempGlobalConfig);
+
+        // We need another copy of global config because we're scheduling some calls instead of
+        // running them in place. We need to be sure that object we send will be handled unchanged.
+        final Configuration configCopy = new Configuration(mGlobalConfiguration);
+        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
+            Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
+            msg.obj = configCopy;
+            msg.arg1 = userId;
+            mHandler.sendMessage(msg);
+        }
+
+        // TODO(multi-display): Clear also on secondary display density change?
+        final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+        if (isDensityChange) {
+            // Reset the unsupported display size dialog.
+            mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG);
+
+            killAllBackgroundProcessesExcept(Build.VERSION_CODES.N,
+                    ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        }
+
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord app = mLruProcesses.get(i);
+            try {
+                if (app.thread != null) {
+                    if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
+                            + app.processName + " new config " + configCopy);
+                    app.thread.scheduleConfigurationChanged(configCopy);
+                }
+            } catch (Exception e) {
+            }
+        }
+
+        Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_FOREGROUND);
+        broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID,
+                UserHandle.USER_ALL);
+        if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+            intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            if (!mProcessesReady) {
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            }
+            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                    AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID,
+                    UserHandle.USER_ALL);
+        }
+
+        // Update the configuration with WM first and check if any of the stacks need to be resized
+        // due to the configuration change. If so, resize the stacks now and do any relaunches if
+        // necessary. This way we don't need to relaunch again afterwards in
+        // ensureActivityConfigurationLocked().
+        if (mWindowManager != null) {
+            final int[] resizedStacks =
+                    mWindowManager.setNewConfiguration(mTempGlobalConfig);
+            if (resizedStacks != null) {
+                for (int stackId : resizedStacks) {
+                    resizeStackWithBoundsFromWindowManager(stackId, deferResume);
                 }
             }
         }
 
+        return changes;
+    }
+
+    /** Applies latest configuration and/or visibility updates if needed. */
+    private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
         boolean kept = true;
         final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
         // mainStack is null during startup.
@@ -19043,22 +19074,28 @@
                         !PRESERVE_WINDOWS);
             }
         }
-        if (mWindowManager != null) {
-            mWindowManager.continueSurfaceLayout();
-        }
+
         return kept;
     }
 
+    /** Helper method that requests bounds from WM and applies them to stack. */
+    private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
+        final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
+        mStackSupervisor.resizeStackLocked(
+                stackId, newBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
+    }
+
     /**
-     * Decide based on the configuration whether we should shouw the ANR,
-     * crash, etc dialogs.  The idea is that if there is no affordence to
+     * Decide based on the configuration whether we should show the ANR,
+     * crash, etc dialogs.  The idea is that if there is no affordance to
      * press the on-screen buttons, or the user experience would be more
      * greatly impacted than the crash itself, we shouldn't show the dialog.
      *
      * A thought: SystemUI might also want to get told about this, the Power
      * dialog / global actions also might want different behaviors.
      */
-    private static final boolean shouldShowDialogs(Configuration config, boolean inVrMode) {
+    private static boolean shouldShowDialogs(Configuration config, boolean inVrMode) {
         final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
                                    && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                                    && config.navigation == Configuration.NAVIGATION_NONAV);
@@ -22173,4 +22210,22 @@
             Binder.restoreCallingIdentity(callingId);
         }
     }
+
+    @Override
+    public boolean canBypassWorkChallenge(PendingIntent intent) throws RemoteException {
+        final int userId = intent.getCreatorUserHandle().getIdentifier();
+        if (!mUserController.isUserRunningLocked(userId, ActivityManager.FLAG_AND_LOCKED)) {
+            return false;
+        }
+        IIntentSender target = intent.getTarget();
+        if (!(target instanceof PendingIntentRecord)) {
+            return false;
+        }
+        final PendingIntentRecord record = (PendingIntentRecord) target;
+        final ResolveInfo rInfo = mStackSupervisor.resolveIntent(record.key.requestIntent,
+                record.key.requestResolvedType, userId, PackageManager.MATCH_DIRECT_BOOT_AWARE);
+        // For direct boot aware activities, they can be shown without triggering a work challenge
+        // before the profile user is unlocked.
+        return rInfo != null && rInfo.activityInfo != null;
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4af6435..caaa77a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.StackId;
+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.PINNED_STACK_ID;
 import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER;
@@ -945,21 +946,29 @@
         // The activity now gets access to the data associated with this Intent.
         service.grantUriPermissionFromIntentLocked(callingUid, packageName,
                 intent, getUriPermissionsLocked(), userId);
-        // We want to immediately deliver the intent to the activity if
-        // it is currently the top resumed activity...  however, if the
-        // device is sleeping, then all activities are stopped, so in that
-        // case we will deliver it if this is the current top activity on its
-        // stack.
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
-        if ((state == ActivityState.RESUMED
-                || (service.isSleepingLocked() && task.stack != null
-                    && task.stack.topRunningActivityLocked() == this))
-                && app != null && app.thread != null) {
+        final ActivityStack stack = task.stack;
+        final boolean isTopActivityInStack =
+                stack != null && stack.topRunningActivityLocked() == this;
+        final boolean isTopActivityWhileSleeping =
+                service.isSleepingLocked() && isTopActivityInStack;
+        final boolean isTopActivityInMinimizedDockedStack = isTopActivityInStack
+                && stack.mStackId == DOCKED_STACK_ID && mStackSupervisor.mIsDockMinimized
+                && state == ActivityState.PAUSED;
+
+        // We want to immediately deliver the intent to the activity if:
+        // - It is the resumed activity.
+        // - The device is sleeping and it is the top activity behind the lock screen (b/6700897).
+        // - It is the top activity in a minimized docked stack. In this case the activity will be
+        //   temporarily resumed then paused again on the client side.
+        if ((state == ActivityState.RESUMED || isTopActivityWhileSleeping
+                || isTopActivityInMinimizedDockedStack) && app != null && app.thread != null) {
             try {
                 ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
                 ar.add(rintent);
-                app.thread.scheduleNewIntent(ar, appToken);
+                app.thread.scheduleNewIntent(
+                        ar, appToken, isTopActivityInMinimizedDockedStack /* andPause */);
                 unsent = false;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 055cd57..b82e28d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -118,6 +118,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 
 import com.android.internal.app.IVoiceInteractor;
@@ -228,6 +229,13 @@
     // stack and the new stack will be on top of all stacks.
     static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
 
+    // The height/width divide used when fitting a task within a bounds with method
+    // {@link #fitWithinBounds}.
+    // We always want the task to to be visible in the bounds without affecting its size when
+    // fitting. To make sure this is the case, we don't adjust the task left or top side pass
+    // the input bounds right or bottom side minus the width or height divided by this value.
+    private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
+
     final ActivityManagerService mService;
     final WindowManagerService mWindowManager;
     private final RecentTasks mRecentTasks;
@@ -321,6 +329,12 @@
     /** The attached Display's unique identifier, or -1 if detached */
     int mDisplayId;
 
+    /** Temp variables used during override configuration update. */
+    private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
+    private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
+    private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
+    private final Rect tempRect2 = new Rect();
+
     /** Run all ActivityStacks through this */
     final ActivityStackSupervisor mStackSupervisor;
 
@@ -1303,7 +1317,9 @@
             // It is possible the activity was freezing the screen before it was paused.
             // In that case go ahead and remove the freeze this activity has on the screen
             // since it is no longer visible.
-            prev.stopFreezingScreenLocked(true /*force*/);
+            if (prev != null) {
+                prev.stopFreezingScreenLocked(true /*force*/);
+            }
             mPausingActivity = null;
         }
 
@@ -2495,7 +2511,7 @@
             boolean notUpdated = true;
             if (mStackSupervisor.isFocusedStack(this)) {
                 Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                        mService.mConfiguration,
+                        mService.mGlobalConfiguration,
                         next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
                 if (config != null) {
                     next.frozenBeforeDestroy = true;
@@ -2550,7 +2566,8 @@
                             break;
                         }
                     }
-                    next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
+                    next.app.thread.scheduleNewIntent(
+                            next.newIntents, next.appToken, false /* andPause */);
                 }
 
                 // Well the app will no longer be stopped.
@@ -4521,6 +4538,86 @@
         }
     }
 
+    /** Update override configurations of all tasks in the stack. */
+    void updateOverrideConfiguration(Rect stackBounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds) {
+
+        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : stackBounds;
+        final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
+
+        mTmpBounds.clear();
+        mTmpConfigs.clear();
+        mTmpInsetBounds.clear();
+
+        for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+            final TaskRecord task = mTaskHistory.get(i);
+            if (task.isResizeable()) {
+                if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+                    // For freeform stack we don't adjust the size of the tasks to match that
+                    // of the stack, but we do try to make sure the tasks are still contained
+                    // with the bounds of the stack.
+                    tempRect2.set(task.mBounds);
+                    fitWithinBounds(tempRect2, stackBounds);
+                    task.updateOverrideConfiguration(tempRect2);
+                } else {
+                    task.updateOverrideConfiguration(taskBounds, insetBounds);
+                }
+            }
+
+            mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+            mTmpBounds.put(task.taskId, task.mBounds);
+            if (tempTaskInsetBounds != null) {
+                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+            }
+        }
+
+        // We might trigger a configuration change. Save the current task bounds for freezing.
+        mWindowManager.prepareFreezingTaskBounds(mStackId);
+        mFullscreen = mWindowManager.resizeStack(mStackId, stackBounds, mTmpConfigs, mTmpBounds,
+                mTmpInsetBounds);
+        setBounds(stackBounds);
+    }
+
+
+    /**
+     * Adjust bounds to stay within stack bounds.
+     *
+     * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
+     * that keep them unchanged, but be contained within the stack bounds.
+     *
+     * @param bounds Bounds to be adjusted.
+     * @param stackBounds Bounds within which the other bounds should remain.
+     */
+    private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
+        if (stackBounds == null || stackBounds.contains(bounds)) {
+            return;
+        }
+
+        if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
+            final int maxRight = stackBounds.right
+                    - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
+            int horizontalDiff = stackBounds.left - bounds.left;
+            if ((horizontalDiff < 0 && bounds.left >= maxRight)
+                    || (bounds.left + horizontalDiff >= maxRight)) {
+                horizontalDiff = maxRight - bounds.left;
+            }
+            bounds.left += horizontalDiff;
+            bounds.right += horizontalDiff;
+        }
+
+        if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
+            final int maxBottom = stackBounds.bottom
+                    - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
+            int verticalDiff = stackBounds.top - bounds.top;
+            if ((verticalDiff < 0 && bounds.top >= maxBottom)
+                    || (bounds.top + verticalDiff >= maxBottom)) {
+                verticalDiff = maxBottom - bounds.top;
+            }
+            bounds.top += verticalDiff;
+            bounds.bottom += verticalDiff;
+        }
+    }
+
     /**
      * Make sure the given activity matches the current configuration. Returns false if the activity
      * had to be destroyed.  Returns true if the configuration is the same, or the activity will
@@ -4540,7 +4637,7 @@
 
         // Short circuit: if the two configurations are equal (the common case), then there is
         // nothing to do.
-        final Configuration newConfig = mService.mConfiguration;
+        final Configuration newConfig = mService.mGlobalConfiguration;
         r.task.sanitizeOverrideConfiguration(newConfig);
         final Configuration taskConfig = r.task.mOverrideConfig;
         if (r.configuration.equals(newConfig)
@@ -4670,7 +4767,7 @@
         // that has come back from the app after going idle.  In that case
         // we just want to leave the official config object now in the
         // activity and do nothing else.
-        int taskChanges = oldTaskOverride.diff(taskConfig);
+        int taskChanges = oldTaskOverride.diff(taskConfig, true /* skipUndefined */);
         // We don't want to use size changes if they don't cross boundaries that are important to
         // the app.
         if ((taskChanges & CONFIG_SCREEN_SIZE) != 0) {
@@ -4689,48 +4786,6 @@
                 taskChanges &= ~CONFIG_SMALLEST_SCREEN_SIZE;
             }
         }
-        return catchConfigChangesFromUnset(taskConfig, oldTaskOverride, taskChanges);
-    }
-
-    private static int catchConfigChangesFromUnset(Configuration taskConfig,
-            Configuration oldTaskOverride, int taskChanges) {
-        if (taskChanges == 0) {
-            // {@link Configuration#diff} doesn't catch changes from unset values.
-            // Check for changes we care about.
-            if (oldTaskOverride.orientation != taskConfig.orientation) {
-                taskChanges |= CONFIG_ORIENTATION;
-            }
-            // We want to explicitly track situations where the size configuration goes from
-            // undefined to defined. We don't care about crossing the threshold in that case,
-            // because there is no threshold.
-            final int oldHeight = oldTaskOverride.screenHeightDp;
-            final int newHeight = taskConfig.screenHeightDp;
-            final int undefinedHeight = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
-            if ((oldHeight == undefinedHeight && newHeight != undefinedHeight)
-                    || (oldHeight != undefinedHeight && newHeight == undefinedHeight)) {
-                taskChanges |= CONFIG_SCREEN_SIZE;
-            }
-            final int oldWidth = oldTaskOverride.screenWidthDp;
-            final int newWidth = taskConfig.screenWidthDp;
-            final int undefinedWidth = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-            if ((oldWidth == undefinedWidth && newWidth != undefinedWidth)
-                    || (oldWidth != undefinedWidth && newWidth == undefinedWidth)) {
-                taskChanges |= CONFIG_SCREEN_SIZE;
-            }
-            final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
-            final int newSmallest = taskConfig.smallestScreenWidthDp;
-            final int undefinedSmallest = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-            if ((oldSmallest == undefinedSmallest && newSmallest != undefinedSmallest)
-                    || (oldSmallest != undefinedSmallest && newSmallest == undefinedSmallest)) {
-                taskChanges |= CONFIG_SMALLEST_SCREEN_SIZE;
-            }
-            final int oldLayout = oldTaskOverride.screenLayout;
-            final int newLayout = taskConfig.screenLayout;
-            if ((oldLayout == SCREENLAYOUT_UNDEFINED && newLayout != SCREENLAYOUT_UNDEFINED)
-                || (oldLayout != SCREENLAYOUT_UNDEFINED && newLayout == SCREENLAYOUT_UNDEFINED)) {
-                taskChanges |= CONFIG_SCREEN_LAYOUT;
-            }
-        }
         return taskChanges;
     }
 
@@ -4770,7 +4825,7 @@
             r.forceNewConfig = false;
             mStackSupervisor.activityRelaunchingLocked(r);
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
-                    !andResume, new Configuration(mService.mConfiguration),
+                    !andResume, new Configuration(mService.mGlobalConfiguration),
                     new Configuration(r.task.mOverrideConfig), preserveWindow);
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 647c2ec..8602bb6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -267,13 +267,6 @@
     /** Action restriction: launching the activity is restricted by an app op. */
     private static final int ACTIVITY_RESTRICTION_APPOP = 2;
 
-    // The height/width divide used when fitting a task within a bounds with method
-    // {@link #fitWithinBounds}.
-    // We always want the task to to be visible in the bounds without affecting its size when
-    // fitting. To make sure this is the case, we don't adjust the task left or top side pass
-    // the input bounds right or bottom side minus the width or height divided by this value.
-    private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
-
     /** Status Bar Service **/
     private IBinder mToken = new Binder();
     private IStatusBarService mStatusBarService;
@@ -402,13 +395,11 @@
     /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
     boolean inResumeTopActivity;
 
-    // temp. rects used during resize calculation so we don't need to create a new object each time.
+    /**
+     * Temporary rect used during docked stack resize calculation so we don't need to create a new
+     * object each time.
+     */
     private final Rect tempRect = new Rect();
-    private final Rect tempRect2 = new Rect();
-
-    private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
-    private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
-    private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
 
     // The default minimal size that will be used if the activity doesn't specify its minimal size.
     // It will be calculated when the default display gets added.
@@ -1191,7 +1182,7 @@
         // just restarting it anyway.
         if (checkConfig) {
             Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                    mService.mConfiguration,
+                    mService.mGlobalConfiguration,
                     r.mayFreezeScreenLocked(app) ? r.appToken : null);
             // Deferring resume here because we're going to launch new activity shortly.
             // We don't want to perform a redundant launch of the same record while ensuring
@@ -1282,7 +1273,8 @@
             }
             app.forceProcessStateUpTo(mService.mTopProcessState);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
-                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
+                    System.identityHashCode(r), r.info,
+                    new Configuration(mService.mGlobalConfiguration),
                     new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                     task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                     newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
@@ -1911,6 +1903,7 @@
         if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
             return null;
         }
+        // TODO(multi-display): Allow creating stacks on secondary displays.
         return createStackOnDisplay(stackId, Display.DEFAULT_DISPLAY, createOnTop);
     }
 
@@ -1922,14 +1915,6 @@
         return allStacks;
     }
 
-    IBinder getHomeActivityToken() {
-        ActivityRecord homeActivity = getHomeActivity();
-        if (homeActivity != null) {
-            return homeActivity.appToken;
-        }
-        return null;
-    }
-
     ActivityRecord getHomeActivity() {
         return getHomeActivityForUser(mCurrentUser);
     }
@@ -2059,39 +2044,7 @@
             return;
         }
 
-        mTmpBounds.clear();
-        mTmpConfigs.clear();
-        mTmpInsetBounds.clear();
-        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
-        final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            final TaskRecord task = tasks.get(i);
-            if (task.isResizeable()) {
-                if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
-                    // For freeform stack we don't adjust the size of the tasks to match that
-                    // of the stack, but we do try to make sure the tasks are still contained
-                    // with the bounds of the stack.
-                    tempRect2.set(task.mBounds);
-                    fitWithinBounds(tempRect2, bounds);
-                    task.updateOverrideConfiguration(tempRect2);
-                } else {
-                    task.updateOverrideConfiguration(taskBounds, insetBounds);
-                }
-            }
-
-            mTmpConfigs.put(task.taskId, task.mOverrideConfig);
-            mTmpBounds.put(task.taskId, task.mBounds);
-            if (tempTaskInsetBounds != null) {
-                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
-            }
-        }
-
-        // We might trigger a configuration change. Save the current task bounds for freezing.
-        mWindowManager.prepareFreezingTaskBounds(stack.mStackId);
-        stack.mFullscreen = mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs,
-                mTmpBounds, mTmpInsetBounds);
-        stack.setBounds(bounds);
+        stack.updateOverrideConfiguration(bounds, tempTaskBounds, tempTaskInsetBounds);
     }
 
     void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
@@ -2272,12 +2225,12 @@
 
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
 
-        final Configuration overrideConfig =  task.updateOverrideConfiguration(bounds);
+        final boolean updatedConfig = task.updateOverrideConfiguration(bounds);
         // This variable holds information whether the configuration didn't change in a significant
         // way and the activity was kept the way it was. If it's false, it means the activity had
         // to be relaunched due to configuration change.
         boolean kept = true;
-        if (overrideConfig != null) {
+        if (updatedConfig) {
             final ActivityRecord r = task.topRunningActivityLocked();
             if (r != null) {
                 final ActivityStack stack = task.stack;
@@ -4370,45 +4323,6 @@
         }
     }
 
-    /**
-     * Adjust bounds to stay within stack bounds.
-     *
-     * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
-     * that keep them unchanged, but be contained within the stack bounds.
-     *
-     * @param bounds Bounds to be adjusted.
-     * @param stackBounds Bounds within which the other bounds should remain.
-     */
-    private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
-        if (stackBounds == null || stackBounds.contains(bounds)) {
-            return;
-        }
-
-        if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
-            final int maxRight = stackBounds.right
-                    - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
-            int horizontalDiff = stackBounds.left - bounds.left;
-            if ((horizontalDiff < 0 && bounds.left >= maxRight)
-                    || (bounds.left + horizontalDiff >= maxRight)) {
-                horizontalDiff = maxRight - bounds.left;
-            }
-            bounds.left += horizontalDiff;
-            bounds.right += horizontalDiff;
-        }
-
-        if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
-            final int maxBottom = stackBounds.bottom
-                    - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
-            int verticalDiff = stackBounds.top - bounds.top;
-            if ((verticalDiff < 0 && bounds.top >= maxBottom)
-                    || (bounds.top + verticalDiff >= maxBottom)) {
-                verticalDiff = maxBottom - bounds.top;
-            }
-            bounds.top += verticalDiff;
-            bounds.bottom += verticalDiff;
-        }
-    }
-
     ActivityStack findStackBehind(ActivityStack stack) {
         // TODO(multi-display): We are only looking for stacks on the default display.
         final ActivityDisplay display = mActivityDisplays.get(Display.DEFAULT_DISPLAY);
@@ -4524,6 +4438,8 @@
      * entry will be the focused activity.
      */
     public List<IBinder> getTopVisibleActivities() {
+        // TODO(multi-display): Get rid of DEFAULT_DISPLAY here. Used in
+        // VoiceInteractionManagerServiceImpl#showSessionLocked.
         final ActivityDisplay display = mActivityDisplays.get(Display.DEFAULT_DISPLAY);
         if (display == null) {
             return Collections.EMPTY_LIST;
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index a8ea5f4..547161a 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -29,6 +29,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -209,6 +210,11 @@
         if (!mService.mUserController.shouldConfirmCredentials(userId)) {
             return null;
         }
+        // Allow direct boot aware activity to be displayed before the user is unlocked.
+        if (aInfo.directBootAware && mService.mUserController.isUserRunningLocked(userId,
+                ActivityManager.FLAG_AND_LOCKED)) {
+            return null;
+        }
         final IIntentSender target = mService.getIntentSenderLocked(
                 INTENT_SENDER_ACTIVITY, callingPackage,
                 Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bd0d9b8..975b23e1 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -99,11 +99,9 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PowerManagerInternal;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -471,7 +469,7 @@
         }
 
         ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
-                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
+                intent, resolvedType, aInfo, mService.mGlobalConfiguration, resultRecord, resultWho,
                 requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
                 options, sourceRecord);
         if (outActivity != null) {
@@ -713,8 +711,8 @@
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
-            ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
-            Bundle bOptions, boolean ignoreTargetSecurity, int userId,
+            ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult,
+            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
             IActivityContainer iContainer, TaskRecord inTask) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
@@ -783,7 +781,8 @@
             } else {
                 stack = container.mStack;
             }
-            stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
+            stack.mConfigWillChange = globalConfig != null
+                    && mService.mGlobalConfiguration.diff(globalConfig) != 0;
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Starting activity when config will change = " + stack.mConfigWillChange);
 
@@ -872,7 +871,7 @@
                 stack.mConfigWillChange = false;
                 if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Updating to new configuration after starting activity.");
-                mService.updateConfigurationLocked(config, null, false);
+                mService.updateConfigurationLocked(globalConfig, null, false);
             }
 
             if (outResult != null) {
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 576f2b2..c19a571 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -22,7 +22,6 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.server.Watchdog;
 
-import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -33,10 +32,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
@@ -59,7 +55,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Set;
-import java.util.concurrent.Semaphore;
 
 import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
@@ -359,7 +354,7 @@
                 return;
             }
 
-            Message msg = Message.obtain();
+            final Message msg = Message.obtain();
             msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
 
             task = data.task;
@@ -773,6 +768,12 @@
             } else if (app.crashing) {
                 Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
                 return;
+            } else if (app.killedByAm) {
+                Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);
+                return;
+            } else if (app.killed) {
+                Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation);
+                return;
             }
 
             // In case we come through here for the same app before completing
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index a54df4b..0b282ed 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -197,8 +197,8 @@
     }
 
     public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
-        CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
-                mService.mConfiguration.smallestScreenWidthDp,
+        CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mGlobalConfiguration.screenLayout,
+                mService.mGlobalConfiguration.smallestScreenWidthDp,
                 (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
         //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
         return ci;
@@ -207,8 +207,8 @@
     public int computeCompatModeLocked(ApplicationInfo ai) {
         boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
         CompatibilityInfo info = new CompatibilityInfo(ai,
-                mService.mConfiguration.screenLayout,
-                mService.mConfiguration.smallestScreenWidthDp, enabled);
+                mService.mGlobalConfiguration.screenLayout,
+                mService.mGlobalConfiguration.smallestScreenWidthDp, enabled);
         if (info.alwaysSupportsScreen()) {
             return ActivityManager.COMPAT_MODE_NEVER;
         }
@@ -408,8 +408,8 @@
             out.startTag(null, "compat-packages");
 
             final IPackageManager pm = AppGlobals.getPackageManager();
-            final int screenLayout = mService.mConfiguration.screenLayout;
-            final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp;
+            final int screenLayout = mService.mGlobalConfiguration.screenLayout;
+            final int smallestScreenWidthDp = mService.mGlobalConfiguration.smallestScreenWidthDp;
             final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
             while (it.hasNext()) {
                 Map.Entry<String, Integer> entry = it.next();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 475b155..2b00f86 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -220,6 +220,7 @@
     void applyDisplaySize(WindowManagerService wm) {
         if (!mHaveDisplaySize) {
             Point p = new Point();
+            // TODO(multi-display): Compute based on sum of all connected displays' resolutions.
             wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
             if (p.x != 0 && p.y != 0) {
                 updateOomLevels(p.x, p.y, true);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0745a85..6cc4d73 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -271,6 +271,7 @@
     // This number will be assigned when we evaluate OOM scores for all visible tasks.
     int mLayerRank = -1;
 
+    /** Contains configurations settings that are different from the parent's configuration. */
     Configuration mOverrideConfig = Configuration.EMPTY;
 
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -575,7 +576,7 @@
      * @return whether the thumbnail was set
      */
     boolean setLastThumbnailLocked(Bitmap thumbnail) {
-        final Configuration serviceConfig = mService.mConfiguration;
+        final Configuration serviceConfig = mService.mGlobalConfiguration;
         int taskWidth = 0;
         int taskHeight = 0;
         if (mBounds != null) {
@@ -1453,9 +1454,9 @@
     /**
      * Update task's override configuration based on the bounds.
      * @param bounds The bounds of the task.
-     * @return Update configuration or null if there is no change.
+     * @return True if the override configuration was updated.
      */
-    Configuration updateOverrideConfiguration(Rect bounds) {
+    boolean updateOverrideConfiguration(Rect bounds) {
         return updateOverrideConfiguration(bounds, null /* insetBounds */);
     }
 
@@ -1465,11 +1466,11 @@
      * @param insetBounds The bounds used to calculate the system insets, which is used here to
      *                    subtract the navigation bar/status bar size from the screen size reported
      *                    to the application. See {@link IActivityManager#resizeDockedStack}.
-     * @return Update configuration or null if there is no change.
+     * @return True if the override configuration was updated.
      */
-    Configuration updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
+    boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
         if (Objects.equals(mBounds, bounds)) {
-            return null;
+            return false;
         }
         final Configuration oldConfig = mOverrideConfig;
         final boolean oldFullscreen = mFullscreen;
@@ -1500,7 +1501,7 @@
             mService.mStackSupervisor.scheduleReportMultiWindowModeChanged(this);
         }
 
-        return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
+        return !mOverrideConfig.equals(oldConfig);
     }
 
     private void subtractNonDecorInsets(Rect inOutBounds, Rect inInsetBounds,
@@ -1538,7 +1539,7 @@
 
         // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen area,
         // i.e. the screen area without the system bars.
-        final Configuration serviceConfig = mService.mConfiguration;
+        final Configuration serviceConfig = mService.mGlobalConfiguration;
         final Configuration config = new Configuration(Configuration.EMPTY);
         // TODO(multidisplay): Update Dp to that of display stack is on.
         final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
@@ -1579,7 +1580,6 @@
         extracted.smallestScreenWidthDp = config.smallestScreenWidthDp;
         extracted.orientation = config.orientation;
         extracted.screenLayout = config.screenLayout;
-        extracted.fontScale = config.fontScale;
         return extracted;
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0d90832..96ba259 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -513,8 +513,11 @@
     private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
 
     // Request to override default use of A2DP for media.
-    // FIXME: remove when MediaRouter does not use setBluetoothA2dpOn() anymore
     private boolean mBluetoothA2dpEnabled;
+    // FIXME: remove when MediaRouter does not use setBluetoothA2dpOn() anymore
+    // state of bluetooth A2DP enable request sen by deprecated APIs setBluetoothA2dpOn() and
+    // isBluettohA2dpOn()
+    private boolean mBluetoothA2dpEnabledExternal;
     private final Object mBluetoothA2dpEnabledLock = new Object();
 
     // Monitoring of audio routes.  Protected by mCurAudioRoutes.
@@ -848,6 +851,12 @@
             RotationHelper.updateOrientation();
         }
 
+        synchronized (mBluetoothA2dpEnabledLock) {
+            AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
+                    mBluetoothA2dpEnabled ?
+                            AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
+        }
+
         synchronized (mSettingsLock) {
             AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
                     mDockAudioMediaEnabled ?
@@ -2715,7 +2724,7 @@
      * @deprecated
      * */
     public void setBluetoothA2dpOn(boolean on) {
-        mBluetoothA2dpEnabled = on;
+        mBluetoothA2dpEnabledExternal = on;
         Log.e(TAG, "setBluetoothA2dpOn() is deprecated, now a no-op",
                 new Exception("Deprecated use of setBluetoothA2dpOn()"));
     }
@@ -2725,7 +2734,7 @@
      * @deprecated
      * */
     public boolean isBluetoothA2dpOn() {
-        return mBluetoothA2dpEnabled;
+        return mBluetoothA2dpEnabledExternal;
     }
 
     /** @see AudioManager#startBluetoothSco() */
@@ -3392,6 +3401,10 @@
     private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted,
             String caller, int flags) {
         int result = FLAG_ADJUST_VOLUME;
+        if (isPlatformTelevision()) {
+            return result;
+        }
+
         int ringerMode = getRingerModeInternal();
 
         switch (ringerMode) {
@@ -3794,6 +3807,11 @@
                 Slog.i(TAG, "setWiredDeviceConnectionState(" + state + " nm: " + name + " addr:"
                         + address + ")");
             }
+            if ((state == 0) && ((type == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
+                    (type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
+                    (type == AudioSystem.DEVICE_OUT_LINE))) {
+                setBluetoothA2dpOnInt(true);
+            }
             int delay = checkSendBecomingNoisyIntent(type, state);
             queueMsgUnderWakeLock(mAudioHandler,
                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
@@ -4774,6 +4792,7 @@
         VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
         sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
+        setBluetoothA2dpOnInt(true);
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
         // Reset A2DP suspend state each time a new sink is connected
@@ -5011,6 +5030,7 @@
                     devices |= dev;
                 }
             }
+
             if (devices == device) {
                 sendMsg(mAudioHandler,
                         MSG_BROADCAST_AUDIO_BECOMING_NOISY,
@@ -5114,6 +5134,11 @@
                 return;
             }
             if (state != 0) {
+                if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
+                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
+                    (device == AudioSystem.DEVICE_OUT_LINE)) {
+                    setBluetoothA2dpOnInt(false);
+                }
                 if ((device & mSafeMediaVolumeDevices) != 0) {
                     sendMsg(mAudioHandler,
                             MSG_CHECK_MUSIC_ACTIVE,
@@ -5579,9 +5604,26 @@
         }
     }
 
+    // Handles request to override default use of A2DP for media.
+    // Must be called synchronized on mConnectedDevices
+    public void setBluetoothA2dpOnInt(boolean on) {
+        synchronized (mBluetoothA2dpEnabledLock) {
+            mBluetoothA2dpEnabled = on;
+            setForceUseInt_SyncDevices(AudioSystem.FOR_MEDIA,
+                    mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
+        }
+    }
+
     // Must be called synchronized on mConnectedDevices
     private void setForceUseInt_SyncDevices(int usage, int config) {
         switch (usage) {
+            case AudioSystem.FOR_MEDIA:
+                if (config == AudioSystem.FORCE_NO_BT_A2DP) {
+                    mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ALL_A2DP;
+                } else { // config == AudioSystem.FORCE_NONE
+                    mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ALL_A2DP;
+                }
+                break;
             case AudioSystem.FOR_DOCK:
                 if (config == AudioSystem.FORCE_ANALOG_DOCK) {
                     mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 49be879..cc181141 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -40,9 +40,9 @@
     private static final String TAG = "MediaFocusControl";
     private static final boolean DEBUG = false;
 
-    private AudioFocusDeathHandler mDeathHandler;
-    private final IAudioFocusDispatcher mFocusDispatcher; // may be null
-    private final IBinder mSourceRef;
+    private AudioFocusDeathHandler mDeathHandler; // may be null
+    private IAudioFocusDispatcher mFocusDispatcher; // may be null
+    private final IBinder mSourceRef; // may be null
     private final String mClientId;
     private final String mPackageName;
     private final int mCallingUid;
@@ -205,6 +205,7 @@
             if (mSourceRef != null && mDeathHandler != null) {
                 mSourceRef.unlinkToDeath(mDeathHandler, 0);
                 mDeathHandler = null;
+                mFocusDispatcher = null;
             }
         } catch (java.util.NoSuchElementException e) {
             Log.e(TAG, "FocusRequester.release() hit ", e);
@@ -275,12 +276,13 @@
             mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
-            if (mFocusDispatcher != null) {
+            final IAudioFocusDispatcher fd = mFocusDispatcher;
+            if (fd != null) {
                 if (DEBUG) {
                     Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
                         + mClientId);
                 }
-                mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId);
+                fd.dispatchAudioFocusChange(focusGain, mClientId);
             }
         } catch (android.os.RemoteException e) {
             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
@@ -311,14 +313,15 @@
                             toAudioFocusInfo(), false /* wasDispatched */);
                     return;
                 }
-                if (mFocusDispatcher != null) {
+                final IAudioFocusDispatcher fd = mFocusDispatcher;
+                if (fd != null) {
                     if (DEBUG) {
                         Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
                             + mClientId);
                     }
                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
                             toAudioFocusInfo(), true /* wasDispatched */);
-                    mFocusDispatcher.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
+                    fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
                 }
             }
         } catch (android.os.RemoteException e) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 278d70b..206834e 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -160,6 +160,7 @@
                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
                             + clientToRemove);
                     stackIterator.remove();
+                    // stack entry not used anymore, clear references
                     fr.release();
                 }
             }
@@ -171,7 +172,7 @@
      * Called synchronized on mAudioFocusLock
      * Remove focus listeners from the focus stack for a particular client when it has died.
      */
-    private void removeFocusStackEntryForClient(IBinder cb) {
+    private void removeFocusStackEntryOnDeath(IBinder cb) {
         // is the owner of the audio focus part of the client to remove?
         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
                 mFocusStack.peek().hasSameBinder(cb);
@@ -181,9 +182,10 @@
         while(stackIterator.hasNext()) {
             FocusRequester fr = stackIterator.next();
             if(fr.hasSameBinder(cb)) {
-                Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for " + cb);
+                Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
                 stackIterator.remove();
-                // the client just died, no need to unlink to its death
+                // stack entry not used anymore, clear references
+                fr.release();
             }
         }
         if (isTopOfStackForClientToRemove) {
@@ -257,14 +259,9 @@
 
         public void binderDied() {
             synchronized(mAudioFocusLock) {
-                Log.w(TAG, "  AudioFocus   audio focus client died");
-                removeFocusStackEntryForClient(mCb);
+                removeFocusStackEntryOnDeath(mCb);
             }
         }
-
-        public IBinder getBinder() {
-            return mCb;
-        }
     }
 
     /**
@@ -420,6 +417,7 @@
             // (premature death == death before abandoning focus)
             // Register for client death notification
             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
+
             try {
                 cb.linkToDeath(afdh, 0);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b0330b9..cb4bb88 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -104,15 +104,18 @@
 // -----------------------------------------------
 // If a network has no chance of satisfying any requests (even if it were to become validated
 // and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
-// If the network ever for any period of time had satisfied a NetworkRequest (i.e. had been
-// the highest scoring that satisfied the NetworkRequest's constraints), but is no longer the
-// highest scoring network for any NetworkRequest, then there will be a 30s pause before
-// ConnectivityService disconnects the NetworkAgent's AsyncChannel.  During this pause the
-// network is considered "lingering".  This pause exists to allow network communication to be
-// wrapped up rather than abruptly terminated.  During this pause if the network begins satisfying
-// a NetworkRequest, ConnectivityService will cancel the future disconnection of the NetworkAgent's
-// AsyncChannel, and the network is no longer considered "lingering".
+//
+// If the network was satisfying a foreground NetworkRequest (i.e. had been the highest scoring that
+// satisfied the NetworkRequest's constraints), but is no longer the highest scoring network for any
+// foreground NetworkRequest, then there will be a 30s pause to allow network communication to be
+// wrapped up rather than abruptly terminated. During this pause the network is said to be
+// "lingering". During this pause if the network begins satisfying a foreground NetworkRequest,
+// ConnectivityService will cancel the future disconnection of the NetworkAgent's AsyncChannel, and
+// the network is no longer considered "lingering". After the linger timer expires, if the network
+// is satisfying one or more background NetworkRequests it is kept up in the background. If it is
+// not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
 public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
+
     public NetworkInfo networkInfo;
     // This Network object should always be used if possible, so as to encourage reuse of the
     // enclosed socket factory and connection pool.  Avoid creating other Network objects.
@@ -227,11 +230,13 @@
 
     // The list of NetworkRequests being satisfied by this Network.
     private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
-    // The list of NetworkRequests that this Network previously satisfied with the highest
-    // score.  A non-empty list indicates that if this Network was validated it is lingered.
+
     // How many of the satisfied requests are actual requests and not listens.
     private int mNumRequestNetworkRequests = 0;
 
+    // How many of the satisfied requests are of type BACKGROUND_REQUEST.
+    private int mNumBackgroundNetworkRequests = 0;
+
     public final Messenger messenger;
     public final AsyncChannel asyncChannel;
 
@@ -265,6 +270,32 @@
     //
     // These functions must only called on ConnectivityService's main thread.
 
+    private static final boolean ADD = true;
+    private static final boolean REMOVE = false;
+
+    private void updateRequestCounts(boolean add, NetworkRequest request) {
+        int delta = add ? +1 : -1;
+        switch (request.type) {
+            case REQUEST:
+            case TRACK_DEFAULT:
+                mNumRequestNetworkRequests += delta;
+                break;
+
+            case BACKGROUND_REQUEST:
+                mNumRequestNetworkRequests += delta;
+                mNumBackgroundNetworkRequests += delta;
+                break;
+
+            case LISTEN:
+                break;
+
+            case NONE:
+            default:
+                Log.wtf(TAG, "Unhandled request type " + request.type);
+                break;
+        }
+    }
+
     /**
      * Add {@code networkRequest} to this network as it's satisfied by this network.
      * @return true if {@code networkRequest} was added or false if {@code networkRequest} was
@@ -273,9 +304,15 @@
     public boolean addRequest(NetworkRequest networkRequest) {
         NetworkRequest existing = mNetworkRequests.get(networkRequest.requestId);
         if (existing == networkRequest) return false;
-        if (existing != null && existing.isRequest()) mNumRequestNetworkRequests--;
+        if (existing != null) {
+            // Should only happen if the requestId wraps. If that happens lots of other things will
+            // be broken as well.
+            Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
+                    networkRequest, existing, name()));
+            updateRequestCounts(REMOVE, existing);
+        }
         mNetworkRequests.put(networkRequest.requestId, networkRequest);
-        if (networkRequest.isRequest()) mNumRequestNetworkRequests++;
+        updateRequestCounts(ADD, networkRequest);
         return true;
     }
 
@@ -285,9 +322,9 @@
     public void removeRequest(int requestId) {
         NetworkRequest existing = mNetworkRequests.get(requestId);
         if (existing == null) return;
+        updateRequestCounts(REMOVE, existing);
         mNetworkRequests.remove(requestId);
         if (existing.isRequest()) {
-            mNumRequestNetworkRequests--;
             unlingerRequest(existing);
         }
     }
@@ -316,12 +353,37 @@
     }
 
     /**
+     * Returns the number of requests currently satisfied by this network of type
+     * {@link android.net.NetworkRequest.Type.BACKGROUND_REQUEST}.
+     */
+    public int numBackgroundNetworkRequests() {
+        return mNumBackgroundNetworkRequests;
+    }
+
+    /**
+     * Returns the number of foreground requests currently satisfied by this network.
+     */
+    public int numForegroundNetworkRequests() {
+        return mNumRequestNetworkRequests - mNumBackgroundNetworkRequests;
+    }
+
+    /**
      * Returns the number of requests of any type currently satisfied by this network.
      */
     public int numNetworkRequests() {
         return mNetworkRequests.size();
     }
 
+    /**
+     * Returns whether the network is a background network. A network is a background network if it
+     * is satisfying no foreground requests and at least one background request. (If it did not have
+     * a background request, it would be a speculative network that is only being kept up because
+     * it might satisfy a request if it validated).
+     */
+    public boolean isBackgroundNetwork() {
+        return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
+    }
+
     // Does this network satisfy request?
     public boolean satisfies(NetworkRequest request) {
         return created &&
@@ -354,16 +416,20 @@
         }
 
         int score = currentScore;
-        // Use NET_CAPABILITY_VALIDATED here instead of lastValidated, this allows
-        // ConnectivityService.updateCapabilities() to compute the old score prior to updating
-        // networkCapabilities (with a potentially different validated state).
-        if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) && !pretendValidated) {
+        if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
             score -= UNVALIDATED_SCORE_PENALTY;
         }
         if (score < 0) score = 0;
         return score;
     }
 
+    // Return true on devices configured to ignore score penalty for wifi networks
+    // that become unvalidated (b/31075769).
+    private boolean ignoreWifiUnvalidationPenalty() {
+        boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+        return isWifi && !mConnService.avoidBadWifi() && everValidated;
+    }
+
     // Get the current score for this Network.  This may be modified from what the
     // NetworkAgent sent, as it has modifiers applied to it.
     public int getCurrentScore() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 99926a9..f7b01be 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -35,7 +35,7 @@
 
 public class NetworkNotificationManager {
 
-    public static enum NotificationType { SIGN_IN, NO_INTERNET, NETWORK_SWITCH };
+    public static enum NotificationType { SIGN_IN, NO_INTERNET, LOST_INTERNET, NETWORK_SWITCH };
 
     private static final String NOTIFICATION_ID = "Connectivity.Notification";
 
@@ -91,8 +91,8 @@
      * @param id an identifier that uniquely identifies this notification.  This must match
      *         between show and hide calls.  We use the NetID value but for legacy callers
      *         we concatenate the range of types with the range of NetIDs.
-     * @param nai the network with which the notification is associated. For a SIGN_IN or
-     *         NO_INTERNET notification, this is the network we're connecting to. For a
+     * @param nai the network with which the notification is associated. For a SIGN_IN, NO_INTERNET,
+     *         or LOST_INTERNET notification, this is the network we're connecting to. For a
      *         NETWORK_SWITCH notification it's the network that we switched from. When this network
      *         disconnects the notification is removed.
      * @param switchToNai for a NETWORK_SWITCH notification, the network we are switching to. Null
@@ -126,6 +126,10 @@
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet, 0);
             details = r.getString(R.string.wifi_no_internet_detailed);
+        } else if (notifyType == NotificationType.LOST_INTERNET &&
+                transportType == TRANSPORT_WIFI) {
+            title = r.getString(R.string.wifi_no_internet, 0);
+            details = r.getString(R.string.wifi_no_internet_detailed);
         } else if (notifyType == NotificationType.SIGN_IN) {
             switch (transportType) {
                 case TRANSPORT_WIFI:
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 7d1da01..58c76ec 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -27,6 +27,7 @@
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -39,10 +40,10 @@
 import com.android.net.IProxyCallback;
 import com.android.net.IProxyPortListener;
 import com.android.net.IProxyService;
-import com.android.server.IoThread;
 
 import libcore.io.Streams;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -66,6 +67,7 @@
     private static final int DELAY_1 = 0;
     private static final int DELAY_4 = 3;
     private static final int DELAY_LONG = 4;
+    private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
     /** Keep these values up-to-date with ProxyService.java */
     public static final String KEY_PROXY = "keyProxy";
@@ -123,15 +125,21 @@
         }
     };
 
+    private final HandlerThread mNetThread = new HandlerThread("android.pacmanager",
+            android.os.Process.THREAD_PRIORITY_DEFAULT);
+    private final Handler mNetThreadHandler;
+
     class PacRefreshIntentReceiver extends BroadcastReceiver {
         public void onReceive(Context context, Intent intent) {
-            IoThread.getHandler().post(mPacDownloader);
+            mNetThreadHandler.post(mPacDownloader);
         }
     }
 
     public PacManager(Context context, Handler handler, int proxyMessage) {
         mContext = context;
         mLastPort = -1;
+        mNetThread.start();
+        mNetThreadHandler = new Handler(mNetThread.getLooper());
 
         mPacRefreshIntent = PendingIntent.getBroadcast(
                 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
@@ -199,7 +207,25 @@
     private static String get(Uri pacUri) throws IOException {
         URL url = new URL(pacUri.toString());
         URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
-        return new String(Streams.readFully(urlConnection.getInputStream()));
+        long contentLength = -1;
+        try {
+            contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
+        } catch (NumberFormatException e) {
+            // Ignore
+        }
+        if (contentLength > MAX_PAC_SIZE) {
+            throw new IOException("PAC too big: " + contentLength + " bytes");
+        }
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
+            bytes.write(buffer, 0, count);
+            if (bytes.size() > MAX_PAC_SIZE) {
+                throw new IOException("PAC too big");
+            }
+        }
+        return bytes.toString();
     }
 
     private int getNextDelay(int currentDelay) {
@@ -267,7 +293,7 @@
         intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
         if ((mProxyConnection != null) && (mConnection != null)) {
             // Already bound no need to bind again, just download the new file.
-            IoThread.getHandler().post(mPacDownloader);
+            mNetThreadHandler.post(mPacDownloader);
             return;
         }
         mConnection = new ServiceConnection() {
@@ -297,7 +323,7 @@
                         } catch (RemoteException e) {
                             Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
                         }
-                        IoThread.getHandler().post(mPacDownloader);
+                        mNetThreadHandler.post(mPacDownloader);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 07048a4..ca64817 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -172,10 +172,12 @@
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
             IVrManager vrManager =
                     (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
-            try {
-                vrManager.registerListener(mVrStateCallbacks);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+            if (vrManager != null) {
+                try {
+                    vrManager.registerListener(mVrStateCallbacks);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 2d71ce9..ede6e30 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -108,7 +108,7 @@
     private CharSequence mQueueTitle;
     private int mRatingType;
     private int mRepeatMode;
-    private boolean mShuffleMode;
+    private boolean mShuffleModeEnabled;
     private long mLastActiveTime;
     // End TransportPerformer fields
 
@@ -649,7 +649,7 @@
             for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
-                    cb.onShuffleModeChanged(mShuffleMode);
+                    cb.onShuffleModeChanged(mShuffleModeEnabled);
                 } catch (DeadObjectException e) {
                     mControllerCallbacks.remove(i);
                     Log.w(TAG, "Removed dead callback in pushShuffleModeUpdate.", e);
@@ -880,11 +880,11 @@
         }
 
         @Override
-        public void setShuffleMode(boolean shuffleMode) {
+        public void setShuffleModeEnabled(boolean enabled) {
             boolean changed;
             synchronized (mLock) {
-                changed = mShuffleMode != shuffleMode;
-                mShuffleMode = shuffleMode;
+                changed = mShuffleModeEnabled != enabled;
+                mShuffleModeEnabled = enabled;
             }
             if (changed) {
                 mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE);
@@ -1115,9 +1115,9 @@
             }
         }
 
-        public void shuffleMode(boolean shuffleMode) {
+        public void shuffleMode(boolean enabled) {
             try {
-                mCb.onShuffleMode(shuffleMode);
+                mCb.onShuffleMode(enabled);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in shuffleMode.", e);
             }
@@ -1364,9 +1364,9 @@
         }
 
         @Override
-        public void shuffleMode(boolean shuffleMode) throws RemoteException {
+        public void shuffleMode(boolean enabled) throws RemoteException {
             updateCallingPackage();
-            mSessionCb.shuffleMode(shuffleMode);
+            mSessionCb.shuffleMode(enabled);
         }
 
 
@@ -1419,8 +1419,8 @@
         }
 
         @Override
-        public boolean getShuffleMode() {
-            return mShuffleMode;
+        public boolean isShuffleModeEnabled() {
+            return mShuffleModeEnabled;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a1245a9..6c10ffd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -168,7 +168,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -402,7 +401,6 @@
 
     private final AppOpsManager mAppOps;
 
-    private final MyPackageMonitor mPackageMonitor;
     private final IPackageManager mIPm;
 
 
@@ -445,8 +443,6 @@
 
         mAppOps = context.getSystemService(AppOpsManager.class);
 
-        mPackageMonitor = new MyPackageMonitor();
-
         // Expose private service for system components to use.
         LocalServices.addService(NetworkPolicyManagerInternal.class,
                 new NetworkPolicyManagerInternalImpl());
@@ -578,8 +574,6 @@
 
             mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
-            mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
-
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
                     updatePowerSaveWhitelistUL();
@@ -761,6 +755,7 @@
             if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
             synchronized (mUidRulesFirstLock) {
                 mUidPolicy.delete(uid);
+                removeRestrictBackgroundWhitelistedUidUL(uid, true, true);
                 updateRestrictionRulesForUidUL(uid);
                 synchronized (mNetworkPoliciesSecondLock) {
                     writePolicyAL();
@@ -1443,8 +1438,14 @@
                 + "; generating default policy");
 
         // Build default mobile policy, and assume usage cycle starts today
-        final long warningBytes = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_networkPolicyDefaultWarning) * MB_IN_BYTES;
+        final int dataWarningConfig = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_networkPolicyDefaultWarning);
+        final long warningBytes;
+        if (dataWarningConfig == WARNING_DISABLED) {
+            warningBytes = WARNING_DISABLED;
+        } else {
+            warningBytes = dataWarningConfig * MB_IN_BYTES;
+        }
 
         final Time time = new Time();
         time.setToNow();
@@ -2651,9 +2652,31 @@
         }
     }
 
+    /**
+     * Toggle the firewall standby chain and inform listeners if the uid rules have effectively
+     * changed.
+     */
     void updateRulesForAppIdleParoleUL() {
-        boolean enableChain = !mUsageStats.isAppIdleParoleOn();
+        boolean paroled = mUsageStats.isAppIdleParoleOn();
+        boolean enableChain = !paroled;
         enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain);
+
+        int ruleCount = mUidFirewallStandbyRules.size();
+        for (int i = 0; i < ruleCount; i++) {
+            int uid = mUidFirewallStandbyRules.keyAt(i);
+            int oldRules = mUidRules.get(uid);
+            if (enableChain) {
+                // Chain wasn't enabled before and the other power-related
+                // chains are whitelists, so we can clear the
+                // MASK_ALL_NETWORKS part of the rules and re-inform listeners if
+                // the effective rules result in blocking network access.
+                oldRules &= MASK_METERED_NETWORKS;
+            } else {
+                // Skip if it had no restrictions to begin with
+                if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
+            }
+            updateRulesForPowerRestrictionsUL(uid, oldRules, paroled);
+        }
     }
 
     /**
@@ -3007,14 +3030,34 @@
      * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
      */
     private void updateRulesForPowerRestrictionsUL(int uid) {
+        final int oldUidRules = mUidRules.get(uid, RULE_NONE);
+
+        final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false);
+
+        if (newUidRules == RULE_NONE) {
+            mUidRules.delete(uid);
+        } else {
+            mUidRules.put(uid, newUidRules);
+        }
+    }
+
+    /**
+     * Similar to above but ignores idle state if app standby is currently disabled by parole.
+     *
+     * @param uid the uid of the app to update rules for
+     * @param oldUidRules the current rules for the uid, in order to determine if there's a change
+     * @param paroled whether to ignore idle state of apps and only look at other restrictions.
+     *
+     * @return the new computed rules for the uid
+     */
+    private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) {
         if (!isUidValidForBlacklistRules(uid)) {
             if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
-            return;
+            return RULE_NONE;
         }
 
-        final boolean isIdle = isUidIdle(uid);
+        final boolean isIdle = !paroled && isUidIdle(uid);
         final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
-        final int oldUidRules = mUidRules.get(uid, RULE_NONE);
         final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
 
         final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid);
@@ -3048,12 +3091,6 @@
                     + ", oldUidRules=" + uidRulesToString(oldUidRules));
         }
 
-        if (newUidRules == RULE_NONE) {
-            mUidRules.delete(uid);
-        } else {
-            mUidRules.put(uid, newUidRules);
-        }
-
         // Second step: notify listeners if state changed.
         if (newRule != oldRule) {
             if (newRule == RULE_NONE || (newRule & RULE_ALLOW_ALL) != 0) {
@@ -3070,6 +3107,8 @@
             }
             mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
         }
+
+        return newUidRules;
     }
 
     private class AppIdleStateChangeListener
@@ -3511,18 +3550,6 @@
         }
     }
 
-    private class MyPackageMonitor extends PackageMonitor {
-
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid);
-            synchronized (mUidRulesFirstLock) {
-                removeRestrictBackgroundWhitelistedUidUL(uid, true, true);
-                updateRestrictionRulesForUidUL(uid);
-            }
-        }
-    }
-
     private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
 
         @Override
diff --git a/services/core/java/com/android/server/notification/ImportanceExtractor.java b/services/core/java/com/android/server/notification/ImportanceExtractor.java
index 885b9b7..3bdc22c 100644
--- a/services/core/java/com/android/server/notification/ImportanceExtractor.java
+++ b/services/core/java/com/android/server/notification/ImportanceExtractor.java
@@ -15,6 +15,7 @@
 */
 package com.android.server.notification;
 
+import android.app.NotificationManager;
 import android.content.Context;
 import android.util.Slog;
 
@@ -41,10 +42,17 @@
             if (DBG) Slog.d(TAG, "missing config");
             return null;
         }
-
-        record.setUserImportance(
-                mConfig.getImportance(record.sbn.getPackageName(), record.sbn.getUid()));
-
+        int importance = NotificationManager.IMPORTANCE_UNSPECIFIED;
+        int appImportance = mConfig.getImportance(
+                record.sbn.getPackageName(), record.sbn.getUid());
+        int channelImportance = record.getChannel().getImportance();
+        if (appImportance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
+            record.setUserImportance(channelImportance);
+        } else if (channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
+            record.setUserImportance(appImportance);
+        } else {
+            record.setUserImportance(Math.min(appImportance, channelImportance));
+        }
         return null;
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 084ba8e..0c7c8aa 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_CHANNEL_BANNED;
 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
@@ -55,6 +56,7 @@
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
@@ -140,7 +142,6 @@
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.vr.VrManagerInternal;
 import com.android.server.notification.ManagedServices.UserProfiles;
 
 import libcore.io.IoUtils;
@@ -232,7 +233,6 @@
     AudioManagerInternal mAudioManagerInternal;
     @Nullable StatusBarManagerInternal mStatusBar;
     Vibrator mVibrator;
-    private VrManagerInternal mVrManagerInternal;
     private WindowManagerInternal mWindowManagerInternal;
 
     final IBinder mForegroundToken = new Binder();
@@ -746,8 +746,8 @@
                 if (pkgList != null && (pkgList.length > 0)) {
                     for (String pkgName : pkgList) {
                         if (cancelNotifications) {
-                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
-                                    changeUserId, reason, null);
+                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
+                                    !queryRestart, changeUserId, reason, null);
                         }
                     }
                 }
@@ -779,13 +779,13 @@
             } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 if (userHandle >= 0) {
-                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
+                    cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
                             REASON_USER_STOPPED, null);
                 }
             } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 if (userHandle >= 0) {
-                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
+                    cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
                             REASON_PROFILE_TURNED_OFF, null);
                 }
             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
@@ -907,6 +907,28 @@
     }
 
     @VisibleForTesting
+    void setStatusBarManager(StatusBarManagerInternal statusBar) {
+        mStatusBar = statusBar;
+    }
+
+    @VisibleForTesting
+    void setLights(Light light) {
+        mNotificationLight = light;
+        mAttentionLight = light;
+    }
+
+    @VisibleForTesting
+    void setScreenOn(boolean on) {
+        mScreenOn = on;
+    }
+
+    @VisibleForTesting
+    void addNotification(NotificationRecord r) {
+        mNotificationList.add(r);
+        mNotificationsByKey.put(r.sbn.getKey(), r);
+    }
+
+    @VisibleForTesting
     void setSystemReady(boolean systemReady) {
         mSystemReady = systemReady;
     }
@@ -1128,7 +1150,6 @@
             // Grab our optional AudioService
             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
             mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
-            mVrManagerInternal = getLocalService(VrManagerInternal.class);
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
             mZenModeHelper.onSystemReady();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -1149,8 +1170,8 @@
 
         // Now, cancel any outstanding notifications that are part of a just-disabled app
         if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
-            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
-                    REASON_PACKAGE_BANNED, null);
+            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
+                    UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
         }
     }
 
@@ -1408,7 +1429,7 @@
             // Calling from user space, don't allow the canceling of actively
             // running foreground services.
             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
-                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
+                    pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
                     REASON_APP_CANCEL_ALL, null);
         }
 
@@ -1486,6 +1507,87 @@
             return mRankingHelper.getImportance(pkg, uid);
         }
 
+        @Override
+        public void createNotificationChannel(String pkg, NotificationChannel channel) {
+            Preconditions.checkNotNull(channel);
+            Preconditions.checkNotNull(channel.getId());
+            Preconditions.checkNotNull(channel.getName());
+            checkCallerIsSystemOrSameApp(pkg);
+            mRankingHelper.createNotificationChannel(pkg, Binder.getCallingUid(), channel);
+            savePolicyFile();
+        }
+
+        @Override
+        public void updateNotificationChannel(String pkg, NotificationChannel channel) {
+            Preconditions.checkNotNull(channel);
+            Preconditions.checkNotNull(channel.getId());
+            checkCallerIsSystemOrSameApp(pkg);
+            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+                // cancel
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
+                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+            }
+            mRankingHelper.updateNotificationChannel(Binder.getCallingUid(), pkg,
+                    Binder.getCallingUid(), channel);
+            savePolicyFile();
+        }
+
+        @Override
+        public NotificationChannel getNotificationChannel(String pkg, String channelId) {
+            Preconditions.checkNotNull(channelId);
+            checkCallerIsSystemOrSameApp(pkg);
+            return mRankingHelper.getNotificationChannel(pkg, Binder.getCallingUid(), channelId);
+        }
+
+        @Override
+        public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
+                String channelId) {
+            Preconditions.checkNotNull(channelId);
+            checkCallerIsSystem();
+            return mRankingHelper.getNotificationChannel(pkg, uid, channelId);
+        }
+
+        @Override
+        public void deleteNotificationChannel(String pkg, String channelId) {
+            Preconditions.checkNotNull(channelId);
+            checkCallerIsSystemOrSameApp(pkg);
+            if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
+                throw new IllegalArgumentException("Cannot delete default channel");
+            }
+            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
+                    UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+            mRankingHelper.deleteNotificationChannel(pkg, Binder.getCallingUid(), channelId);
+            savePolicyFile();
+        }
+
+        @Override
+        public void updateNotificationChannelForPackage(String pkg, int uid,
+                NotificationChannel channel) {
+            Preconditions.checkNotNull(channel);
+            Preconditions.checkNotNull(channel.getId());
+            checkCallerIsSystem();
+            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+                // cancel
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
+                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+            }
+            mRankingHelper.updateNotificationChannel(Binder.getCallingUid(), pkg, uid, channel);
+            savePolicyFile();
+        }
+
+        @Override
+        public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
+                int uid) {
+            checkCallerIsSystem();
+            return mRankingHelper.getNotificationChannels(pkg, uid);
+        }
+
+        @Override
+        public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
+            checkCallerIsSystemOrSameApp(pkg);
+            return mRankingHelper.getNotificationChannels(pkg, Binder.getCallingUid());
+        }
+
         /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
@@ -1955,6 +2057,29 @@
             });
         }
 
+        @Override
+        public void requestUnbindProvider(IConditionProvider provider) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                // allow bound services to disable themselves
+                final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
+                info.getOwner().setComponentState(info.component, false);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void requestBindProvider(ComponentName component) {
+            checkCallerIsSystemOrSameApp(component.getPackageName());
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mConditionProviders.setComponentState(component, true);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
         private void enforceSystemOrSystemUIOrVolume(String message) {
             if (mAudioManagerInternal != null) {
                 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
@@ -2312,7 +2437,10 @@
                                         summaryNotification, adjustedSbn.getUser(),
                                         newAutoBundleKey,
                                         System.currentTimeMillis());
-                        summaryRecord = new NotificationRecord(getContext(), summarySbn);
+                        summaryRecord = new NotificationRecord(getContext(), summarySbn,
+                                mRankingHelper.getNotificationChannel(adjustedSbn.getPackageName(),
+                                        adjustedSbn.getUid(),
+                                        adjustedSbn.getNotification().getNotificationChannel()));
                         summaries.put(adjustment.getPackage(), summarySbn.getKey());
                     }
                 }
@@ -2616,7 +2744,9 @@
                 Notification.PRIORITY_MAX);
 
         // setup local book-keeping
-        final NotificationRecord r = new NotificationRecord(getContext(), n);
+        final NotificationRecord r = new NotificationRecord(getContext(), n,
+                mRankingHelper.getNotificationChannel(pkg, callingUid,
+                        n.getNotification().getNotificationChannel()));
         mHandler.post(new EnqueueNotificationRunnable(userId, r));
 
         idOut[0] = id;
@@ -2674,7 +2804,8 @@
                 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
 
                 // blocked apps
-                if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
+                if (r.getImportance() == NotificationManager.IMPORTANCE_NONE
+                        || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE
                         || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
                     if (!isSystemNotification) {
                         if (isPackageSuspended) {
@@ -2842,9 +2973,8 @@
             // DEFAULT_SOUND or because notification.sound is pointing at
             // Settings.System.NOTIFICATION_SOUND)
             final boolean useDefaultSound =
-                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
-                           Settings.System.DEFAULT_NOTIFICATION_URI
-                                   .equals(notification.sound);
+                    (notification.defaults & Notification.DEFAULT_SOUND) != 0
+                    || Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound);
 
             Uri soundUri = null;
             if (useDefaultSound) {
@@ -2855,6 +2985,9 @@
             } else if (notification.sound != null) {
                 soundUri = notification.sound;
                 hasValidSound = (soundUri != null);
+            } else if (record.getChannel().getDefaultRingtone() != null) {
+                soundUri = record.getChannel().getDefaultRingtone();
+                hasValidSound = (soundUri != null);
             }
 
             // Does the notification want to specify its own vibration?
@@ -2871,8 +3004,10 @@
             final boolean useDefaultVibrate =
                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
 
+            final boolean hasChannelVibration = record.getChannel().shouldVibrate();
+
             hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
-                    hasCustomVibrate;
+                    hasCustomVibrate || hasChannelVibration;
 
             // We can alert, and we're allowed to alert, but if the developer asked us to only do
             // it once, and we already have, then don't.
@@ -2908,26 +3043,13 @@
                         }
                     }
                 }
-
                 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
                         == AudioManager.RINGER_MODE_SILENT)) {
                     mVibrateNotificationKey = key;
 
                     if (useDefaultVibrate || convertSoundToVibration) {
-                        // Escalate privileges so we can use the vibrator even if the
-                        // notifying app does not have the VIBRATE permission.
-                        long identity = Binder.clearCallingIdentity();
-                        try {
-                            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
-                                    useDefaultVibrate ? mDefaultVibrationPattern
-                                            : mFallbackVibrationPattern,
-                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
-                                            ? 0: -1, audioAttributesForNotification(notification));
-                            buzz = true;
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    } else if (notification.vibrate.length > 1) {
+                        playNonCustomVibration(record, useDefaultVibrate);
+                    } else if (notification.vibrate != null && notification.vibrate.length > 1) {
                         // If you want your own vibration pattern, you need the VIBRATE
                         // permission
                         mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
@@ -2935,6 +3057,8 @@
                                 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
                                         ? 0: -1, audioAttributesForNotification(notification));
                         buzz = true;
+                    } else if (hasChannelVibration) {
+                        playNonCustomVibration(record, useDefaultVibrate);
                     }
                 }
             }
@@ -2952,7 +3076,7 @@
         // light
         // release the light
         boolean wasShowLights = mLights.remove(key);
-        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
+        if (shouldShowLights(record) && aboveThreshold
                 && ((record.getSuppressedVisualEffects()
                 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
             mLights.add(key);
@@ -2976,6 +3100,28 @@
         }
     }
 
+    private boolean shouldShowLights(final NotificationRecord record) {
+        return record.getChannel().shouldShowLights()
+                || (record.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0;
+    }
+
+    private boolean playNonCustomVibration(final NotificationRecord record,
+            boolean useDefaultVibrate) {
+        // Escalate privileges so we can use the vibrator even if the
+        // notifying app does not have the VIBRATE permission.
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                    useDefaultVibrate ? mDefaultVibrationPattern
+                            : mFallbackVibrationPattern,
+                    ((record.getNotification().flags & Notification.FLAG_INSISTENT) != 0)
+                            ? 0: -1, audioAttributesForNotification(record.getNotification()));
+            return true;
+        } finally{
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     private static AudioAttributes audioAttributesForNotification(Notification n) {
         if (n.audioAttributes != null
                 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
@@ -3466,8 +3612,8 @@
      * Cancels all notifications from a given package that have all of the
      * {@code mustHaveFlags}.
      */
-    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
-            int mustNotHaveFlags, boolean doit, int userId, int reason,
+    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
+            int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
             ManagedServiceInfo listener) {
         String listenerName = listener == null ? null : listener.component.toShortString();
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
@@ -3495,6 +3641,9 @@
                 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
                     continue;
                 }
+                if (channelId == null || !channelId.equals(r.getChannel().getId())) {
+                    continue;
+                }
                 if (canceledNotifications == null) {
                     canceledNotifications = new ArrayList<>();
                 }
@@ -3613,7 +3762,8 @@
             int ledARGB = ledno.ledARGB;
             int ledOnMS = ledno.ledOnMS;
             int ledOffMS = ledno.ledOffMS;
-            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
+            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0
+                    || (ledno.flags & Notification.FLAG_SHOW_LIGHTS) == 0) {
                 ledARGB = mDefaultNotificationColor;
                 ledOnMS = mDefaultNotificationLedOn;
                 ledOffMS = mDefaultNotificationLedOff;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index b2198d7..c5de93e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -23,12 +23,14 @@
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
+import android.net.Uri;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -101,8 +103,11 @@
     private String mUserExplanation;
     private String mPeopleExplanation;
 
+    private NotificationChannel mNotificationChannel;
+
     @VisibleForTesting
-    public NotificationRecord(Context context, StatusBarNotification sbn)
+    public NotificationRecord(Context context, StatusBarNotification sbn,
+            NotificationChannel channel)
     {
         this.sbn = sbn;
         mOriginalFlags = sbn.getNotification().flags;
@@ -111,6 +116,7 @@
         mUpdateTimeMs = mCreationTimeMs;
         mContext = context;
         stats = new NotificationUsageStats.SingleNotificationStats();
+        mNotificationChannel = channel;
         mImportance = defaultImportance();
     }
 
@@ -145,7 +151,9 @@
         boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0
                 || (n.defaults & Notification.DEFAULT_VIBRATE) != 0
                 || n.sound != null
-                || n.vibrate != null;
+                || n.vibrate != null
+                || mNotificationChannel.shouldVibrate()
+                || mNotificationChannel.getDefaultRingtone() != null;
         stats.isNoisy = isNoisy;
 
         if (!isNoisy && importance > IMPORTANCE_LOW) {
@@ -283,6 +291,7 @@
         pw.println(prefix + "  mVisibleSinceMs=" + mVisibleSinceMs);
         pw.println(prefix + "  mUpdateTimeMs=" + mUpdateTimeMs);
         pw.println(prefix + "  mSuppressedVisualEffects= " + mSuppressedVisualEffects);
+        pw.println(prefix + "  mNotificationChannel= " + mNotificationChannel);
     }
 
 
@@ -527,4 +536,8 @@
     public boolean isImportanceFromUser() {
         return mImportance == mUserImportance;
     }
+
+    public NotificationChannel getChannel() {
+        return mNotificationChannel;
+    }
 }
diff --git a/services/core/java/com/android/server/notification/PriorityExtractor.java b/services/core/java/com/android/server/notification/PriorityExtractor.java
index 6c76476..666cf00d 100644
--- a/services/core/java/com/android/server/notification/PriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/PriorityExtractor.java
@@ -15,6 +15,7 @@
 */
 package com.android.server.notification;
 
+import android.app.Notification;
 import android.content.Context;
 import android.util.Slog;
 
@@ -42,8 +43,11 @@
             return null;
         }
 
-        record.setPackagePriority(
-                mConfig.getPriority(record.sbn.getPackageName(), record.sbn.getUid()));
+        int priority = mConfig.getPriority(record.sbn.getPackageName(), record.sbn.getUid());
+        if (priority == Notification.PRIORITY_DEFAULT && record.getChannel().canBypassDnd()){
+            priority = Notification.PRIORITY_MAX;
+        }
+        record.setPackagePriority(priority);
 
         return null;
     }
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b5cc2ef..2df4043 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.notification;
 
+import android.app.NotificationChannel;
+import android.content.pm.ParceledListSlice;
+
 public interface RankingConfig {
 
     int getPriority(String packageName, int uid);
@@ -28,4 +31,10 @@
     void setImportance(String packageName, int uid, int importance);
 
     int getImportance(String packageName, int uid);
+
+    void createNotificationChannel(String pkg, int uid, NotificationChannel channel);
+    void updateNotificationChannel(int callingUid, String pkg, int uid, NotificationChannel channel);
+    NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
+    void deleteNotificationChannel(String pkg, int uid, String channelId);
+    ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid);
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 32c5b13..7182da1 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -16,16 +16,19 @@
 package com.android.server.notification;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
+import android.os.Process;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
 
-import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.internal.R;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -38,6 +41,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -48,15 +52,15 @@
 
     private static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
-    private static final String ATT_VERSION = "version";
+    private static final String TAG_CHANNEL = "channel";
 
+    private static final String ATT_VERSION = "version";
     private static final String ATT_NAME = "name";
     private static final String ATT_UID = "uid";
+    private static final String ATT_ID = "id";
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
-    private static final String ATT_TOPIC_ID = "id";
-    private static final String ATT_TOPIC_LABEL = "label";
 
     private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
     private static final int DEFAULT_VISIBILITY = Ranking.VISIBILITY_NO_OVERRIDE;
@@ -166,6 +170,28 @@
                         r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                         r.priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
                         r.visibility = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+
+                        final int innerDepth = parser.getDepth();
+                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                                && (type != XmlPullParser.END_TAG
+                                || parser.getDepth() > innerDepth)) {
+                            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                                continue;
+                            }
+
+                            String tagName = parser.getName();
+                            if (TAG_CHANNEL.equals(tagName)) {
+                                String id = parser.getAttributeValue(null, ATT_ID);
+                                CharSequence channelName = parser.getAttributeValue(null, ATT_NAME);
+
+                                if (!TextUtils.isEmpty(id)) {
+                                    final NotificationChannel channel =
+                                            new NotificationChannel(id, channelName);
+                                    channel.populateFromXml(parser);
+                                    r.channels.put(id, channel);
+                                }
+                            }
+                        }
                     }
                 }
             }
@@ -184,11 +210,18 @@
             r = new Record();
             r.pkg = pkg;
             r.uid = uid;
+            NotificationChannel defaultChannel = createDefaultChannel();
+            r.channels.put(defaultChannel.getId(), defaultChannel);
             mRecords.put(key, r);
         }
         return r;
     }
 
+    private NotificationChannel createDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID,
+                mContext.getString(R.string.default_notification_channel_label));
+    }
+
     public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
         out.startTag(null, TAG_RANKING);
         out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
@@ -201,7 +234,8 @@
                 continue;
             }
             final boolean hasNonDefaultSettings = r.importance != DEFAULT_IMPORTANCE
-                    || r.priority != DEFAULT_PRIORITY || r.visibility != DEFAULT_VISIBILITY;
+                    || r.priority != DEFAULT_PRIORITY || r.visibility != DEFAULT_VISIBILITY
+                    || r.channels.size() > 0;
             if (hasNonDefaultSettings) {
                 out.startTag(null, TAG_PACKAGE);
                 out.attribute(null, ATT_NAME, r.pkg);
@@ -219,6 +253,10 @@
                     out.attribute(null, ATT_UID, Integer.toString(r.uid));
                 }
 
+                for (NotificationChannel channel : r.channels.values()) {
+                    channel.writeXml(out);
+                }
+
                 out.endTag(null, TAG_PACKAGE);
             }
         }
@@ -309,11 +347,6 @@
         }
     }
 
-    private static boolean tryParseBool(String value, boolean defValue) {
-        if (TextUtils.isEmpty(value)) return defValue;
-        return Boolean.parseBoolean(value);
-    }
-
     /**
      * Gets priority.
      */
@@ -356,6 +389,65 @@
         return getOrCreateRecord(packageName, uid).importance;
     }
 
+    @Override
+    public void createNotificationChannel(String pkg, int uid, NotificationChannel channel) {
+        Record r = getOrCreateRecord(pkg, uid);
+        if (r.channels.containsKey(channel.getId()) || channel.getName().equals(
+                mContext.getString(R.string.default_notification_channel_label))) {
+            throw new IllegalArgumentException("Channel already exists");
+        }
+        if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+            channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
+        }
+        r.channels.put(channel.getId(), channel);
+        updateConfig();
+    }
+
+    @Override
+    public void updateNotificationChannel(int callingUid, String pkg, int uid,
+            NotificationChannel updatedChannel) {
+        Record r = getOrCreateRecord(pkg, uid);
+        NotificationChannel channel = r.channels.get(updatedChannel.getId());
+        if (channel == null) {
+            throw new IllegalArgumentException("Channel does not exist");
+        }
+        if (!isUidSystem(callingUid)) {
+            updatedChannel.setImportance(channel.getImportance());
+            updatedChannel.setName(channel.getName());
+        }
+        if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+            updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
+        }
+        r.channels.put(updatedChannel.getId(), updatedChannel);
+        updateConfig();
+    }
+
+    @Override
+    public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId) {
+        Record r = getOrCreateRecord(pkg, uid);
+        if (channelId == null) {
+            channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+        }
+        return r.channels.get(channelId);
+    }
+
+    @Override
+    public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+        Record r = getOrCreateRecord(pkg, uid);
+        r.channels.remove(channelId);
+    }
+
+    @Override
+    public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid) {
+        List<NotificationChannel> channels = new ArrayList<>();
+        Record r = getOrCreateRecord(pkg, uid);
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            channels.add(r.channels.valueAt(i));
+        }
+        return new ParceledListSlice<NotificationChannel>(channels);
+    }
+
     /**
      * Sets importance.
      */
@@ -420,6 +512,12 @@
                     pw.print(Notification.visibilityToString(r.visibility));
                 }
                 pw.println();
+                for (NotificationChannel channel : r.channels.values()) {
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.print("  ");
+                    pw.println(channel);
+                }
             }
         }
     }
@@ -449,6 +547,9 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         record.put("visibility", Notification.visibilityToString(r.visibility));
                     }
+                    for (NotificationChannel channel : r.channels.values()) {
+                        record.put("channel", channel.toJson());
+                    }
                 } catch (JSONException e) {
                    // pass
                 }
@@ -530,6 +631,11 @@
         }
     }
 
+    private static boolean isUidSystem(int uid) {
+        final int appid = UserHandle.getAppId(uid);
+        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
+    }
+
     private static class Record {
         static int UNKNOWN_UID = UserHandle.USER_NULL;
 
@@ -538,5 +644,7 @@
         int importance = DEFAULT_IMPORTANCE;
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
+
+        ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
    }
 }
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 32d03ce..50a51b2 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -40,7 +40,9 @@
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.List;
 import java.util.TimeZone;
 
 /**
@@ -91,12 +93,14 @@
         pw.print("      mRegistered="); pw.println(mRegistered);
         pw.println("      mSubscriptions=");
         final long now = System.currentTimeMillis();
-        for (Uri conditionId : mSubscriptions.keySet()) {
-            pw.print("        ");
-            pw.print(meetsSchedule(mSubscriptions.get(conditionId), now) ? "* " : "  ");
-            pw.println(conditionId);
-            pw.print("            ");
-            pw.println(mSubscriptions.get(conditionId).toString());
+        synchronized (mSubscriptions) {
+            for (Uri conditionId : mSubscriptions.keySet()) {
+                pw.print("        ");
+                pw.print(meetsSchedule(mSubscriptions.get(conditionId), now) ? "* " : "  ");
+                pw.println(conditionId);
+                pw.print("            ");
+                pw.println(mSubscriptions.get(conditionId).toString());
+            }
         }
         pw.println("      snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozed));
         dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now);
@@ -125,17 +129,21 @@
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
         if (!ZenModeConfig.isValidScheduleConditionId(conditionId)) {
-            notifyCondition(conditionId, Condition.STATE_FALSE, "badCondition");
+            notifyCondition(createCondition(conditionId, Condition.STATE_FALSE, "badCondition"));
             return;
         }
-        mSubscriptions.put(conditionId, toScheduleCalendar(conditionId));
+        synchronized (mSubscriptions) {
+            mSubscriptions.put(conditionId, toScheduleCalendar(conditionId));
+        }
         evaluateSubscriptions();
     }
 
     @Override
     public void onUnsubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
-        mSubscriptions.remove(conditionId);
+        synchronized (mSubscriptions) {
+            mSubscriptions.remove(conditionId);
+        }
         removeSnoozed(conditionId);
         evaluateSubscriptions();
     }
@@ -154,36 +162,43 @@
         if (mAlarmManager == null) {
             mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         }
-        setRegistered(!mSubscriptions.isEmpty());
         final long now = System.currentTimeMillis();
         mNextAlarmTime = 0;
         long nextUserAlarmTime = getNextAlarm();
-        for (Uri conditionId : mSubscriptions.keySet()) {
-            final ScheduleCalendar cal = mSubscriptions.get(conditionId);
-            if (cal != null && cal.isInSchedule(now)) {
-                if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) {
-                    notifyCondition(conditionId, Condition.STATE_FALSE, "alarmCanceled");
-                    addSnoozed(conditionId);
-                } else {
-                    notifyCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule");
-                }
-                cal.maybeSetNextAlarm(now, nextUserAlarmTime);
-            } else {
-                notifyCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
-                removeSnoozed(conditionId);
-                if (nextUserAlarmTime == 0) {
+        List<Condition> conditionsToNotify = new ArrayList<>();
+        synchronized (mSubscriptions) {
+            setRegistered(!mSubscriptions.isEmpty());
+            for (Uri conditionId : mSubscriptions.keySet()) {
+                final ScheduleCalendar cal = mSubscriptions.get(conditionId);
+                if (cal != null && cal.isInSchedule(now)) {
+                    if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) {
+                        conditionsToNotify.add(createCondition(
+                                conditionId, Condition.STATE_FALSE, "alarmCanceled"));
+                        addSnoozed(conditionId);
+                    } else {
+                        conditionsToNotify.add(createCondition(
+                                conditionId, Condition.STATE_TRUE, "meetsSchedule"));
+                    }
                     cal.maybeSetNextAlarm(now, nextUserAlarmTime);
+                } else {
+                    conditionsToNotify.add(createCondition(
+                            conditionId, Condition.STATE_FALSE, "!meetsSchedule"));
+                    removeSnoozed(conditionId);
+                    if (cal != null && nextUserAlarmTime == 0) {
+                        cal.maybeSetNextAlarm(now, nextUserAlarmTime);
+                    }
                 }
-            }
-            if (cal != null) {
-                final long nextChangeTime = cal.getNextChangeTime(now);
-                if (nextChangeTime > 0 && nextChangeTime > now) {
-                    if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
-                        mNextAlarmTime = nextChangeTime;
+                if (cal != null) {
+                    final long nextChangeTime = cal.getNextChangeTime(now);
+                    if (nextChangeTime > 0 && nextChangeTime > now) {
+                        if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
+                            mNextAlarmTime = nextChangeTime;
+                        }
                     }
                 }
             }
         }
+        notifyConditions(conditionsToNotify.toArray(new Condition[conditionsToNotify.size()]));
         updateAlarm(now, mNextAlarmTime);
     }
 
@@ -240,14 +255,10 @@
         }
     }
 
-    private void notifyCondition(Uri conditionId, int state, String reason) {
-        if (DEBUG) Slog.d(TAG, "notifyCondition " + conditionId
+    private Condition createCondition(Uri id, int state, String reason) {
+        if (DEBUG) Slog.d(TAG, "notifyCondition " + id
                 + " " + Condition.stateToString(state)
                 + " reason=" + reason);
-        notifyCondition(createCondition(conditionId, state));
-    }
-
-    private Condition createCondition(Uri id, int state) {
         final String summary = NOT_SHOWN;
         final String line1 = NOT_SHOWN;
         final String line2 = NOT_SHOWN;
@@ -315,10 +326,12 @@
         public void onReceive(Context context, Intent intent) {
             if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction());
             if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
-                for (Uri conditionId : mSubscriptions.keySet()) {
-                    final ScheduleCalendar cal = mSubscriptions.get(conditionId);
-                    if (cal != null) {
-                        cal.setTimeZone(Calendar.getInstance().getTimeZone());
+                synchronized (mSubscriptions) {
+                    for (Uri conditionId : mSubscriptions.keySet()) {
+                        final ScheduleCalendar cal = mSubscriptions.get(conditionId);
+                        if (cal != null) {
+                            cal.setTimeZone(Calendar.getInstance().getTimeZone());
+                        }
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/notification/VisibilityExtractor.java b/services/core/java/com/android/server/notification/VisibilityExtractor.java
index 2da2b2f..9d0e506 100644
--- a/services/core/java/com/android/server/notification/VisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/VisibilityExtractor.java
@@ -16,6 +16,7 @@
 package com.android.server.notification;
 
 import android.content.Context;
+import android.service.notification.NotificationListenerService;
 import android.util.Slog;
 
 /**
@@ -42,8 +43,12 @@
             return null;
         }
 
-        record.setPackageVisibilityOverride(
-                mConfig.getVisibilityOverride(record.sbn.getPackageName(), record.sbn.getUid()));
+        int visibility =
+                mConfig.getVisibilityOverride(record.sbn.getPackageName(), record.sbn.getUid());
+        if (visibility == NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
+            visibility = record.getChannel().getLockscreenVisibility();
+        }
+        record.setPackageVisibilityOverride(visibility);
 
         return null;
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 7badecf..63c0baf 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -102,7 +102,7 @@
     @Override
     public void onServiceAdded(ComponentName component) {
         if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
-        mHelper.setConfigAsync(mHelper.getConfig(), "zmc.onServiceAdded");
+        mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded");
     }
 
     @Override
@@ -116,7 +116,7 @@
             updated |= updateSnoozing(automaticRule);
         }
         if (updated) {
-            mHelper.setConfigAsync(config, "conditionChanged");
+            mHelper.setConfig(config, "conditionChanged");
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4393761..29fa754 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -35,6 +35,7 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioSystem;
@@ -490,7 +491,6 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mZenMode=");
         pw.println(Global.zenModeToString(mZenMode));
-        dump(pw, prefix, "mDefaultConfig", mDefaultConfig);
         final int N = mConfigs.size();
         for (int i = 0; i < N; i++) {
             dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
@@ -529,7 +529,7 @@
 
     public void readXml(XmlPullParser parser, boolean forRestore)
             throws XmlPullParserException, IOException {
-        final ZenModeConfig config = ZenModeConfig.readXml(parser, mConfigMigration);
+        final ZenModeConfig config = ZenModeConfig.readXml(parser);
         if (config != null) {
             if (forRestore) {
                 //TODO: http://b/22388012
@@ -619,8 +619,10 @@
         return setConfigLocked(config, reason, true /*setRingerMode*/);
     }
 
-    public void setConfigAsync(ZenModeConfig config, String reason) {
-        mHandler.postSetConfig(config, reason);
+    public void setConfig(ZenModeConfig config, String reason) {
+        synchronized (mConfig) {
+            setConfigLocked(config, reason);
+        }
     }
 
     private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
@@ -736,13 +738,14 @@
         // total silence restrictions
         final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
 
-        for (int i = USAGE_UNKNOWN; i <= USAGE_VIRTUAL_SOURCE; i++) {
-            if (i == USAGE_NOTIFICATION) {
-                applyRestrictions(muteNotifications || muteEverything, i);
-            } else if (i == USAGE_NOTIFICATION_RINGTONE) {
-                applyRestrictions(muteCalls || muteEverything, i);
+        for (int usage : AudioAttributes.SDK_USAGES) {
+            final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
+            if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) {
+                applyRestrictions(muteNotifications || muteEverything, usage);
+            } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
+                applyRestrictions(muteCalls || muteEverything, usage);
             } else {
-                applyRestrictions(muteEverything, i);
+                applyRestrictions(muteEverything, usage);
             }
         }
     }
@@ -806,7 +809,7 @@
         try {
             parser = resources.getXml(R.xml.default_zen_mode_config);
             while (parser.next() != XmlPullParser.END_DOCUMENT) {
-                final ZenModeConfig config = ZenModeConfig.readXml(parser, mConfigMigration);
+                final ZenModeConfig config = ZenModeConfig.readXml(parser);
                 if (config != null) return config;
             }
         } catch (Exception e) {
@@ -878,45 +881,6 @@
         }
     }
 
-    private final ZenModeConfig.Migration mConfigMigration = new ZenModeConfig.Migration() {
-        @Override
-        public ZenModeConfig migrate(ZenModeConfig.XmlV1 v1) {
-            if (v1 == null) return null;
-            final ZenModeConfig rt = new ZenModeConfig();
-            rt.allowCalls = v1.allowCalls;
-            rt.allowEvents = v1.allowEvents;
-            rt.allowCallsFrom = v1.allowFrom;
-            rt.allowMessages = v1.allowMessages;
-            rt.allowMessagesFrom = v1.allowFrom;
-            rt.allowReminders = v1.allowReminders;
-            // don't migrate current exit condition
-            final int[] days = ZenModeConfig.XmlV1.tryParseDays(v1.sleepMode);
-            if (days != null && days.length > 0) {
-                Log.i(TAG, "Migrating existing V1 downtime to single schedule");
-                final ScheduleInfo schedule = new ScheduleInfo();
-                schedule.days = days;
-                schedule.startHour = v1.sleepStartHour;
-                schedule.startMinute = v1.sleepStartMinute;
-                schedule.endHour = v1.sleepEndHour;
-                schedule.endMinute = v1.sleepEndMinute;
-                final ZenRule rule = new ZenRule();
-                rule.enabled = true;
-                rule.name = mContext.getResources()
-                        .getString(R.string.zen_mode_downtime_feature_name);
-                rule.conditionId = ZenModeConfig.toScheduleConditionId(schedule);
-                rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
-                        : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-                rule.component = ScheduleConditionProvider.COMPONENT;
-                rt.automaticRules.put(ZenModeConfig.newRuleId(), rule);
-            } else {
-                Log.i(TAG, "No existing V1 downtime found, generating default schedules");
-                appendDefaultScheduleRules(rt);
-            }
-            appendDefaultEventRules(rt);
-            return rt;
-        }
-    };
-
     private final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
         @Override
         public String toString() {
@@ -1082,7 +1046,6 @@
     private final class H extends Handler {
         private static final int MSG_DISPATCH = 1;
         private static final int MSG_METRICS = 2;
-        private static final int MSG_SET_CONFIG = 3;
         private static final int MSG_APPLY_CONFIG = 4;
 
         private final class ConfigMessageData {
@@ -1090,12 +1053,6 @@
             public final String reason;
             public final boolean setRingerMode;
 
-            ConfigMessageData(ZenModeConfig config, String reason) {
-                this.config = config;
-                this.reason = reason;
-                this.setRingerMode = false;
-            }
-
             ConfigMessageData(ZenModeConfig config, String reason, boolean setRingerMode) {
                 this.config = config;
                 this.reason = reason;
@@ -1119,10 +1076,6 @@
             sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
         }
 
-        private void postSetConfig(ZenModeConfig config, String reason) {
-            sendMessage(obtainMessage(MSG_SET_CONFIG, new ConfigMessageData(config, reason)));
-        }
-
         private void postApplyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
             sendMessage(obtainMessage(MSG_APPLY_CONFIG,
                     new ConfigMessageData(config, reason, setRingerMode)));
@@ -1137,12 +1090,6 @@
                 case MSG_METRICS:
                     mMetrics.emit();
                     break;
-                case MSG_SET_CONFIG:
-                    ConfigMessageData configData = (ConfigMessageData) msg.obj;
-                    synchronized (mConfig) {
-                        setConfigLocked(configData.config, configData.reason);
-                    }
-                    break;
                 case MSG_APPLY_CONFIG:
                     ConfigMessageData applyConfigData = (ConfigMessageData) msg.obj;
                     applyConfig(applyConfigData.config, applyConfigData.reason,
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 8d926f5..68b465a 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -60,7 +60,7 @@
 
     public EphemeralResolverConnection(Context context, ComponentName componentName) {
         mContext = context;
-        mIntent = new Intent().setComponent(componentName);
+        mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName);
     }
 
     public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 72c549f..2e18b1c 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -230,6 +230,11 @@
         mInstaller.execute("move_ab", apkPath, instructionSet, outputPath);
     }
 
+    public void deleteOdex(String apkPath, String instructionSet, String outputPath)
+            throws InstallerException {
+        mInstaller.execute("delete_odex", apkPath, instructionSet, outputPath);
+    }
+
     private static void assertValidInstructionSet(String instructionSet)
             throws InstallerException {
         for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 37c54cf..48e000d8 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -631,17 +631,20 @@
             public void onPackageAdded(String packageName, int uid) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackageAdded")) continue;
-                    try {
-                        listener.onPackageAdded(user, packageName);
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackageAdded")) continue;
+                        try {
+                            listener.onPackageAdded(user, packageName);
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackageAdded(packageName, uid);
             }
@@ -650,17 +653,20 @@
             public void onPackageRemoved(String packageName, int uid) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackageRemoved")) continue;
-                    try {
-                        listener.onPackageRemoved(user, packageName);
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackageRemoved")) continue;
+                        try {
+                            listener.onPackageRemoved(user, packageName);
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackageRemoved(packageName, uid);
             }
@@ -669,17 +675,20 @@
             public void onPackageModified(String packageName) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackageModified")) continue;
-                    try {
-                        listener.onPackageChanged(user, packageName);
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackageModified")) continue;
+                        try {
+                            listener.onPackageChanged(user, packageName);
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackageModified(packageName);
             }
@@ -688,17 +697,20 @@
             public void onPackagesAvailable(String[] packages) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackagesAvailable")) continue;
-                    try {
-                        listener.onPackagesAvailable(user, packages, isReplacing());
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesAvailable")) continue;
+                        try {
+                            listener.onPackagesAvailable(user, packages, isReplacing());
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackagesAvailable(packages);
             }
@@ -707,17 +719,20 @@
             public void onPackagesUnavailable(String[] packages) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnavailable")) continue;
-                    try {
-                        listener.onPackagesUnavailable(user, packages, isReplacing());
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnavailable")) continue;
+                        try {
+                            listener.onPackagesUnavailable(user, packages, isReplacing());
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackagesUnavailable(packages);
             }
@@ -726,17 +741,20 @@
             public void onPackagesSuspended(String[] packages) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackagesSuspended")) continue;
-                    try {
-                        listener.onPackagesSuspended(user, packages);
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesSuspended")) continue;
+                        try {
+                            listener.onPackagesSuspended(user, packages);
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackagesSuspended(packages);
             }
@@ -745,17 +763,20 @@
             public void onPackagesUnsuspended(String[] packages) {
                 UserHandle user = new UserHandle(getChangingUserId());
                 final int n = mListeners.beginBroadcast();
-                for (int i = 0; i < n; i++) {
-                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                    BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                    if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnsuspended")) continue;
-                    try {
-                        listener.onPackagesUnsuspended(user, packages);
-                    } catch (RemoteException re) {
-                        Slog.d(TAG, "Callback failed ", re);
+                try {
+                    for (int i = 0; i < n; i++) {
+                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                        if (!isEnabledProfileOf(user, cookie.user, "onPackagesUnsuspended")) continue;
+                        try {
+                            listener.onPackagesUnsuspended(user, packages);
+                        } catch (RemoteException re) {
+                            Slog.d(TAG, "Callback failed ", re);
+                        }
                     }
+                } finally {
+                    mListeners.finishBroadcast();
                 }
-                mListeners.finishBroadcast();
 
                 super.onPackagesUnsuspended(packages);
             }
@@ -768,10 +789,10 @@
 
             private void onShortcutChangedInner(@NonNull String packageName,
                     @UserIdInt int userId) {
+                final int n = mListeners.beginBroadcast();
                 try {
                     final UserHandle user = UserHandle.of(userId);
 
-                    final int n = mListeners.beginBroadcast();
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
@@ -803,10 +824,11 @@
                             Slog.d(TAG, "Callback failed ", re);
                         }
                     }
-                    mListeners.finishBroadcast();
                 } catch (RuntimeException e) {
                     // When the user is locked we get IllegalState, so just catch all.
                     Log.w(TAG, e.getMessage(), e);
+                } finally {
+                    mListeners.finishBroadcast();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index bff6d2d..42079fb 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -31,7 +31,7 @@
 import android.os.storage.StorageManager;
 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;
 
@@ -40,6 +40,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A service for A/B OTA dexopting.
@@ -53,6 +54,10 @@
     // The synthetic library dependencies denoting "no checks."
     private final static String[] NO_LIBRARIES = new String[] { "&" };
 
+    // The amount of "available" (free - low threshold) space necessary at the start of an OTA to
+    // not bulk-delete unused apps' odex files.
+    private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024;  // 1GB.
+
     private final Context mContext;
     private final PackageManagerService mPackageManagerService;
 
@@ -65,6 +70,25 @@
 
     private int completeSize;
 
+    // MetricsLogger properties.
+
+    // Space before and after.
+    private long availableSpaceBefore;
+    private long availableSpaceAfterBulkDelete;
+    private long availableSpaceAfterDexopt;
+
+    // Packages.
+    private int importantPackageCount;
+    private int otherPackageCount;
+
+    // Number of dexopt commands. This may be different from the count of packages.
+    private int dexoptCommandCountTotal;
+    private int dexoptCommandCountExecuted;
+
+    // For spent time.
+    private long otaDexoptTimeStart;
+
+
     public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
         this.mContext = context;
         this.mPackageManagerService = packageManagerService;
@@ -128,6 +152,18 @@
                     generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
         }
         completeSize = mDexoptCommands.size();
+
+        long spaceAvailable = getAvailableSpace();
+        if (spaceAvailable < BULK_DELETE_THRESHOLD) {
+            Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
+                    + PackageManagerServiceUtils.packagesToString(others));
+            for (PackageParser.Package pkg : others) {
+                deleteOatArtifactsOfPackage(pkg);
+            }
+        }
+        long spaceAvailableNow = getAvailableSpace();
+
+        prepareMetricsLogging(important.size(), others.size(), spaceAvailable, spaceAvailableNow);
     }
 
     @Override
@@ -136,6 +172,8 @@
             Log.i(TAG, "Cleaning up OTA Dexopt state.");
         }
         mDexoptCommands = null;
+
+        performMetricsLogging();
     }
 
     @Override
@@ -169,28 +207,67 @@
 
         String next = mDexoptCommands.remove(0);
 
-        if (IsFreeSpaceAvailable()) {
+        if (getAvailableSpace() > 0) {
+            dexoptCommandCountExecuted++;
+
             return next;
         } else {
+            if (DEBUG_DEXOPT) {
+                Log.w(TAG, "Not enough space for OTA dexopt, stopping with "
+                        + (mDexoptCommands.size() + 1) + " commands left.");
+            }
             mDexoptCommands.clear();
             return "(no free space)";
         }
     }
 
-    /**
-     * Check for low space. Returns true if there's space left.
-     */
-    private boolean IsFreeSpaceAvailable() {
-        // TODO: If apps are not installed in the internal /data partition, we should compare
-        //       against that storage's free capacity.
+    private long getMainLowSpaceThreshold() {
         File dataDir = Environment.getDataDirectory();
         @SuppressWarnings("deprecation")
         long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
         if (lowThreshold == 0) {
             throw new IllegalStateException("Invalid low memory threshold");
         }
+        return lowThreshold;
+    }
+
+    /**
+     * Returns the difference of free space to the low-storage-space threshold. Positive values
+     * indicate free bytes.
+     */
+    private long getAvailableSpace() {
+        // TODO: If apps are not installed in the internal /data partition, we should compare
+        //       against that storage's free capacity.
+        long lowThreshold = getMainLowSpaceThreshold();
+
+        File dataDir = Environment.getDataDirectory();
         long usableSpace = dataDir.getUsableSpace();
-        return (usableSpace >= lowThreshold);
+
+        return usableSpace - lowThreshold;
+    }
+
+    private static String getOatDir(PackageParser.Package pkg) {
+        if (!pkg.canHaveOatDir()) {
+            return null;
+        }
+        File codePath = new File(pkg.codePath);
+        if (codePath.isDirectory()) {
+            return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
+        }
+        return null;
+    }
+
+    private void deleteOatArtifactsOfPackage(PackageParser.Package pkg) {
+        String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+        for (String codePath : pkg.getAllCodePaths()) {
+            for (String isa : instructionSets) {
+                try {
+                    mPackageManagerService.mInstaller.deleteOdex(codePath, isa, getOatDir(pkg));
+                } catch (InstallerException e) {
+                    Log.e(TAG, "Failed deleting oat files for " + codePath, e);
+                }
+            }
+        }
     }
 
     /**
@@ -271,6 +348,55 @@
         }
     }
 
+    /**
+     * Initialize logging fields.
+     */
+    private void prepareMetricsLogging(int important, int others, long spaceBegin, long spaceBulk) {
+        availableSpaceBefore = spaceBegin;
+        availableSpaceAfterBulkDelete = spaceBulk;
+        availableSpaceAfterDexopt = 0;
+
+        importantPackageCount = important;
+        otherPackageCount = others;
+
+        dexoptCommandCountTotal = mDexoptCommands.size();
+        dexoptCommandCountExecuted = 0;
+
+        otaDexoptTimeStart = System.nanoTime();
+    }
+
+    private static int inMegabytes(long value) {
+        long in_mega_bytes = value / (1024 * 1024);
+        if (in_mega_bytes > Integer.MAX_VALUE) {
+            Log.w(TAG, "Recording " + in_mega_bytes + "MB of free space, overflowing range");
+            return Integer.MAX_VALUE;
+        }
+        return (int)in_mega_bytes;
+    }
+
+    private void performMetricsLogging() {
+        long finalTime = System.nanoTime();
+
+        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
+                inMegabytes(availableSpaceBefore));
+        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
+                inMegabytes(availableSpaceAfterBulkDelete));
+        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
+                inMegabytes(availableSpaceAfterDexopt));
+
+        MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
+                importantPackageCount);
+        MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
+
+        MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
+        MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
+                dexoptCommandCountExecuted);
+
+        final int elapsedTimeSeconds =
+                (int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
+        MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
+    }
+
     private static class OTADexoptPackageDexOptimizer extends
             PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d98539e..2642520 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -212,6 +212,7 @@
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.MathUtils;
+import android.util.Pair;
 import android.util.PrintStreamPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -366,8 +367,8 @@
     /** REMOVE. According to Svet, this was only used to reset permissions during development. */
     static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
 
-    // STOPSHIP; b/30256615
-    private static final boolean DISABLE_EPHEMERAL_APPS = !Build.IS_DEBUGGABLE;
+    private static final boolean DISABLE_EPHEMERAL_APPS = false;
+    private static final boolean HIDE_EPHEMERAL_APIS = true;
 
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
@@ -3180,7 +3181,7 @@
         // reader
         synchronized (mPackages) {
             for (int i=names.length-1; i>=0; i--) {
-                String cur = mSettings.getRenamedPackage(names[i]);
+                String cur = mSettings.getRenamedPackageLPr(names[i]);
                 out[i] = cur != null ? cur : names[i];
             }
         }
@@ -6301,7 +6302,7 @@
 
     @Override
     public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
-        if (isEphemeralDisabled()) {
+        if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return null;
         }
 
@@ -6325,7 +6326,7 @@
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "isEphemeral");
-        if (isEphemeralDisabled()) {
+        if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return false;
         }
 
@@ -6343,7 +6344,7 @@
 
     @Override
     public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
-        if (isEphemeralDisabled()) {
+        if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return null;
         }
 
@@ -6361,7 +6362,7 @@
 
     @Override
     public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
-        if (isEphemeralDisabled()) {
+        if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return true;
         }
 
@@ -6379,7 +6380,7 @@
 
     @Override
     public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
-        if (isEphemeralDisabled()) {
+        if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
             return null;
         }
 
@@ -6845,7 +6846,7 @@
         // reader
         synchronized (mPackages) {
             // Look to see if we already know about this package.
-            String oldName = mSettings.getRenamedPackage(pkg.packageName);
+            String oldName = mSettings.getRenamedPackageLPr(pkg.packageName);
             if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
                 // This package has been renamed to its original name.  Let's
                 // use that.
@@ -8174,7 +8175,7 @@
             if (pkg.mOriginalPackages != null) {
                 // This package may need to be renamed to a previously
                 // installed name.  Let's check on that...
-                final String renamed = mSettings.getRenamedPackage(pkg.mRealPackage);
+                final String renamed = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
                 if (pkg.mOriginalPackages.contains(renamed)) {
                     // This package had originally been installed as the
                     // original name, and we have already taken care of
@@ -8232,18 +8233,42 @@
                 }
             }
 
-            // Just create the setting, don't add it yet. For already existing packages
-            // the PkgSetting exists already and doesn't have to be created.
-            pkgSetting = mSettings.getPackageWithBenefitsLPw(pkg, origPackage, realName, suid,
-                    destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
-                    pkg.applicationInfo.primaryCpuAbi,
-                    pkg.applicationInfo.secondaryCpuAbi,
-                    pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
-                    user);
-            if (pkgSetting == null) {
-                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                        "Creating application package " + pkg.packageName + " failed");
+            pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+            if (pkgSetting != null && pkgSetting.sharedUser != suid) {
+                PackageManagerService.reportSettingsProblem(Log.WARN,
+                        "Package " + pkg.packageName + " shared user changed from "
+                        + (pkgSetting.sharedUser != null ? pkgSetting.sharedUser.name : "<nothing>")
+                        + " to "
+                        + (suid != null ? suid.name : "<nothing>")
+                        + "; replacing with new");
+                pkgSetting = null;
             }
+            final PackageSetting oldPkgSetting =
+                    pkgSetting == null ? null : new PackageSetting(pkgSetting);
+            final PackageSetting disabledPkgSetting =
+                    mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+            if (pkgSetting == null) {
+                final String parentPackageName = (pkg.parentPackage != null)
+                        ? pkg.parentPackage.packageName : null;
+                pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
+                        disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
+                        pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
+                        pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,
+                        pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
+                        true /*allowInstall*/, parentPackageName, pkg.getChildPackageNames(),
+                        UserManagerService.getInstance());
+                if (origPackage != null) {
+                    mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
+                }
+                mSettings.addUserToSettingLPw(pkgSetting);
+            } else {
+                Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, suid, destCodeFile,
+                        pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,
+                        pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,
+                        pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),
+                        UserManagerService.getInstance());
+            }
+            mSettings.writeUserRestrictions(pkgSetting, oldPkgSetting);
 
             if (pkgSetting.origPackage != null) {
                 // If we are first transitioning from an original package,
@@ -11309,6 +11334,19 @@
 
     private static final class EphemeralIntentResolver
             extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveInfo> {
+        /**
+         * The result that has the highest defined order. Ordering applies on a
+         * per-package basis. Mapping is from package name to Pair of order and
+         * EphemeralResolveInfo.
+         * <p>
+         * NOTE: This is implemented as a field variable for convenience and efficiency.
+         * By having a field variable, we're able to track filter ordering as soon as
+         * a non-zero order is defined. Otherwise, multiple loops across the result set
+         * would be needed to apply ordering. If the intent resolver becomes re-entrant,
+         * this needs to be contained entirely within {@link #filterResults()}.
+         */
+        final ArrayMap<String, Pair<Integer, EphemeralResolveInfo>> mOrderResult = new ArrayMap<>();
+
         @Override
         protected EphemeralResolveIntentInfo[] newArray(int size) {
             return new EphemeralResolveIntentInfo[size];
@@ -11325,7 +11363,51 @@
             if (!sUserManager.exists(userId)) {
                 return null;
             }
-            return info.getEphemeralResolveInfo();
+            final String packageName = info.getEphemeralResolveInfo().getPackageName();
+            final Integer order = info.getOrder();
+            final Pair<Integer, EphemeralResolveInfo> lastOrderResult =
+                    mOrderResult.get(packageName);
+            // ordering is enabled and this item's order isn't high enough
+            if (lastOrderResult != null && lastOrderResult.first >= order) {
+                return null;
+            }
+            final EphemeralResolveInfo res = info.getEphemeralResolveInfo();
+            if (order > 0) {
+                // non-zero order, enable ordering
+                mOrderResult.put(packageName, new Pair<>(order, res));
+            }
+            return res;
+        }
+
+        @Override
+        protected void filterResults(List<EphemeralResolveInfo> results) {
+            // only do work if ordering is enabled [most of the time it won't be]
+            if (mOrderResult.size() == 0) {
+                return;
+            }
+            int resultSize = results.size();
+            for (int i = 0; i < resultSize; i++) {
+                final EphemeralResolveInfo info = results.get(i);
+                final String packageName = info.getPackageName();
+                final Pair<Integer, EphemeralResolveInfo> savedInfo = mOrderResult.get(packageName);
+                if (savedInfo == null) {
+                    // package doesn't having ordering
+                    continue;
+                }
+                if (savedInfo.second == info) {
+                    // circled back to the highest ordered item; remove from order list
+                    mOrderResult.remove(savedInfo);
+                    if (mOrderResult.size() == 0) {
+                        // no more ordered items
+                        break;
+                    }
+                    continue;
+                }
+                // item has a worse order, remove it from the result list
+                results.remove(i);
+                resultSize--;
+                i--;
+            }
         }
     }
 
@@ -14127,7 +14209,7 @@
         if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
 
         synchronized(mPackages) {
-            final String renamedPackage = mSettings.getRenamedPackage(pkgName);
+            final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
             if (renamedPackage != null) {
                 // A package with the same name is already installed, though
                 // it has been renamed to an older name.  The package we
@@ -14994,7 +15076,7 @@
         synchronized (mPackages) {
             // Check if installing already existing package
             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
-                String oldName = mSettings.getRenamedPackage(pkgName);
+                String oldName = mSettings.getRenamedPackageLPr(pkgName);
                 if (pkg.mOriginalPackages != null
                         && pkg.mOriginalPackages.contains(oldName)
                         && mPackages.containsKey(oldName)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9a8ebed..cfd0af7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -168,23 +168,8 @@
                 packageManagerService);
 
         if (DEBUG_DEXOPT) {
-            StringBuilder sb = new StringBuilder();
-            for (PackageParser.Package pkg : result) {
-                if (sb.length() > 0) {
-                    sb.append(", ");
-                }
-                sb.append(pkg.packageName);
-            }
-            Log.i(TAG, "Packages to be dexopted: " + sb.toString());
-
-            sb.setLength(0);
-            for (PackageParser.Package pkg : remainingPkgs) {
-                if (sb.length() > 0) {
-                    sb.append(", ");
-                }
-                sb.append(pkg.packageName);
-            }
-            Log.i(TAG, "Packages skipped from dexopt: " + sb.toString());
+            Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
+            Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
         }
 
         return result;
@@ -201,4 +186,15 @@
             throw ee.rethrowAsIOException();
         }
     }
+
+    public static String packagesToString(Collection<PackageParser.Package> c) {
+        StringBuilder sb = new StringBuilder();
+        for (PackageParser.Package pkg : c) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(pkg.packageName);
+        }
+        return sb.toString();
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index a4604a6..80a398a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -29,10 +29,14 @@
 final class PackageSetting extends PackageSettingBase {
     int appId;
     PackageParser.Package pkg;
+    /**
+     * WARNING. The object reference is important. We perform integer equality and NOT
+     * object equality to check whether shared user settings are the same.
+     */
     SharedUserSetting sharedUser;
     /**
      * Temporary holding space for the shared user ID. While parsing package settings, the
-     * shared users tag may be after the packages. In this case, we must delay linking the
+     * shared users tag may come after the packages. In this case, we must delay linking the
      * shared user setting with the package setting. The shared user ID lets us link the
      * two objects.
      */
@@ -54,7 +58,17 @@
      * Note that it keeps the same PackageParser.Package instance.
      */
     PackageSetting(PackageSetting orig) {
-        super(orig);
+        super(orig, orig.realName);
+        doCopy(orig);
+    }
+
+    /**
+     * New instance of PackageSetting replicating the original settings, but, allows specifying
+     * a real package name.
+     * Note that it keeps the same PackageParser.Package instance.
+     */
+    PackageSetting(PackageSetting orig, String realPkgName) {
+        super(orig, realPkgName);
         doCopy(orig);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index c08bc57..3ad2ae1 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -27,6 +27,8 @@
 import android.util.ArraySet;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
@@ -99,7 +101,7 @@
 
     boolean uidError;
 
-    PackageSignatures signatures = new PackageSignatures();
+    PackageSignatures signatures;
 
     boolean installPermissionsFixed;
 
@@ -147,11 +149,17 @@
                 secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
     }
 
-    /** New instance of PackageSetting with one-level-deep cloning. */
-    PackageSettingBase(PackageSettingBase base) {
+    /**
+     * New instance of PackageSetting with one-level-deep cloning.
+     * <p>
+     * IMPORTANT: With a shallow copy, we do NOT create new contained objects.
+     * This means, for example, changes to the user state of the original PackageSetting
+     * will also change the user state in its copy.
+     */
+    PackageSettingBase(PackageSettingBase base, String realName) {
         super(base);
         name = base.name;
-        realName = base.realName;
+        this.realName = realName;
         doCopy(base);
     }
 
@@ -167,6 +175,7 @@
         this.secondaryCpuAbiString = secondaryCpuAbiString;
         this.cpuAbiOverrideString = cpuAbiOverrideString;
         this.versionCode = pVersionCode;
+        this.signatures = new PackageSignatures();
     }
 
     public void setInstallerPackageName(String packageName) {
@@ -280,6 +289,12 @@
         return readUserState(userId).installed;
     }
 
+    /** Only use for testing. Do NOT use in production code. */
+    @VisibleForTesting
+    SparseArray<PackageUserState> getUserState() {
+        return userState;
+    }
+
     boolean isAnyInstalled(int[] users) {
         for (int user: users) {
             if (readUserState(user).installed) {
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 8f9968ec..8a427cd 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -140,6 +140,36 @@
         }
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PermissionsState other = (PermissionsState) obj;
+
+        if (mPermissions == null) {
+            if (other.mPermissions != null) {
+                return false;
+            }
+        } else if (!mPermissions.equals(other.mPermissions)) {
+            return false;
+        }
+        if (mPermissionReviewRequired == null) {
+            if (other.mPermissionReviewRequired != null) {
+                return false;
+            }
+        } else if (!mPermissionReviewRequired.equals(other.mPermissionReviewRequired)) {
+            return false;
+        }
+        return Arrays.equals(mGlobalGids, other.mGlobalGids);
+    }
+
     public boolean isPermissionReviewRequired(int userId) {
         return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
     }
@@ -566,6 +596,7 @@
         return PERMISSION_OPERATION_SUCCESS;
     }
 
+    // TODO: fix this to use arraycopy and append all ints in one go
     private static int[] appendInts(int[] current, int[] added) {
         if (current != null && added != null) {
             for (int guid : added) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 52313b1..27ac3ad 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -24,6 +24,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
@@ -428,33 +429,22 @@
         mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
     }
 
-    PackageSetting getPackageLPw(String pkgName) {
+    PackageSetting getPackageLPr(String pkgName) {
         return peekPackageLPr(pkgName);
     }
 
-    PackageSetting getPackageWithBenefitsLPw(PackageParser.Package pkg, PackageSetting origPackage,
-            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi,
-            int pkgFlags, int pkgPrivateFlags, UserHandle user)
-                    throws PackageManagerException {
-        final String name = pkg.packageName;
-        final String parentPackageName = (pkg.parentPackage != null)
-                ? pkg.parentPackage.packageName : null;
-        PackageSetting p = getPackageWithBenefitsLPw(name, origPackage, realName, sharedUser,
-                codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbi,
-                secondaryCpuAbi, pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user,
-                true /*allowInstall*/, parentPackageName, pkg.getChildPackageNames());
-        return p;
-    }
-
     PackageSetting peekPackageLPr(String pkgName) {
         return mPackages.get(pkgName);
     }
 
-    String getRenamedPackage(String pkgName) {
+    String getRenamedPackageLPr(String pkgName) {
         return mRenamedPackages.get(pkgName);
     }
 
+    String addRenamedPackageLPw(String pkgName, String origPkgName) {
+        return mRenamedPackages.put(pkgName, origPkgName);
+    }
+
     void setInstallStatus(String pkgName, final int status) {
         PackageSetting p = mPackages.get(pkgName);
         if(p != null) {
@@ -684,280 +674,101 @@
         }
     }
 
-    private PackageSetting getPackageWithBenefitsLPw(String name, PackageSetting origPackage,
-            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String legacyNativeLibraryPathString, String primaryCpuAbiString,
-            String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
-            UserHandle installUser, boolean allowInstall, String parentPackage,
-            List<String> childPackageNames) throws PackageManagerException {
-        final UserManagerService userManager = UserManagerService.getInstance();
-        final PackageSetting disabledPackage = getDisabledSystemPkgLPr(name);
-        final PackageSetting peekPackage = peekPackageLPr(name);
-        final PackageSetting oldPackage =
-                peekPackage == null ? null : new PackageSetting(peekPackage);
-        final PackageSetting p = updatePackageSetting(peekPackage, name, realName,
-                origPackage, disabledPackage, sharedUser, codePath, resourcePath,
-                legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, vc,
-                pkgFlags, pkgPrivateFlags, installUser, allowInstall, parentPackage,
-                childPackageNames, userManager);
-        final boolean newPackageCreated = (peekPackage == null || p != peekPackage);
-        final boolean renamedPackage = newPackageCreated && origPackage != null;
-        if (renamedPackage) {
-            mRenamedPackages.put(name, origPackage.name);
-        }
-        if (newPackageCreated) {
-            if (p.appId == 0) {
-                // Assign new user ID
-                p.appId = newUserIdLPw(p);
-            } else {
-                // Add new setting to list of user IDs
-                addUserIdLPw(p.appId, p, name);
-            }
-            if (p.appId < 0) {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Package " + name + " could not be assigned a valid uid");
-                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                        "Creating application package " + name + " failed");
-            }
-        }
-        if (peekPackageLPr(name) != null) {
-            final List<UserInfo> allUsers = getAllUsers(UserManagerService.getInstance());
-            if (allUsers != null) {
-                for (UserInfo user : allUsers) {
-                    final PackageUserState oldUserState = oldPackage == null
-                            ? PackageSettingBase.DEFAULT_USER_STATE
-                            : oldPackage.readUserState(user.id);
-                    if (!oldUserState.equals(p.readUserState(user.id))) {
-                        writePackageRestrictionsLPr(user.id);
-                    }
-                }
-            }
-        }
-        return p;
-    }
-
     /**
-     * Updates the given package setting using the provided information.
-     * <p>
-     * WARNING: The provided PackageSetting object may be mutated.
-     *
-     * @param pkgSetting The package setting to update. If {@code null} a new PackageSetting
-     * object is created and populated.
-     * @param pkgName The name of the package.
-     * @param realPkgName The real name of the package. A package can change its name during
-     * an update. In that case, all references are to the package's original name, but, we
-     * still need it's real [new] name.
-     * @param originalPkg If the package name has changed, the package setting for the originally
-     * installed package. May be {@code null}.
-     * @param disabledPkg If the package is a system package that has been updated, the package
-     * setting for the disabled system package. May be {@code null}.
-     * @param sharedUser Shared user settings if the package declares a shared user ID.
-     * May be {@code null}.
-     * @param codePath The path to the applications code.
-     * @param resourcePath The path to the application's resources.
-     * @param legacyNativeLibraryPath The path where native libraries are unpacked.
-     * @param primaryCpuAbi The primary CPU architecture. May be {@code null}.
-     * @param secondaryCpuAbi The secondary CPU architecture, if one is defined.
-     * May be {@code null}.
-     * @param versionCode The version code of the package.
-     * @param pkgFlags Application flags for the package. {@link ApplicationInfo#flags}.
-     * @param pkgPrivateFlags Private application flags for the package.
-     * {@link ApplicationInfo#privateFlags}.
-     * @param installUser The user to install the package for. May be {@code null}.
-     * @param allowInstall Whether or not the user state for a newly created package setting can
-     * be installed.
-     * @param parentPkgName The name of the parent package. May be {@code null}.
-     * @param childPkgNames A list of child package names. May be {@code null}.
-     * @param userManager The user manager service.
-     * @return An updated package setting. It may be different than pkgSetting even when pkgSetting
-     * is not {@code null}.
-     * @throws PackageManagerException
+     * Creates a new {@code PackageSetting} object.
+     * Use this method instead of the constructor to ensure a settings object is created
+     * with the correct base.
      */
-    static PackageSetting updatePackageSetting(@Nullable PackageSetting pkgSetting,
-            @NonNull String pkgName, @Nullable String realPkgName,
-            @Nullable PackageSetting originalPkg, @Nullable PackageSetting disabledPkg,
-            @Nullable SharedUserSetting sharedUser, @NonNull File codePath,
-            @NonNull File resourcePath, @Nullable String legacyNativeLibraryPath,
-            @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int versionCode,
-            int pkgFlags, int pkgPrivateFlags, @Nullable UserHandle installUser,
-            boolean allowInstall, @Nullable String parentPkgName,
-            @Nullable List<String> childPkgNames, @NonNull UserManagerService userManager)
-                    throws PackageManagerException {
-        if (pkgSetting != null) {
-            if (pkgSetting.sharedUser != sharedUser) {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Package " + pkgName + " shared user changed from "
-                        + (pkgSetting.sharedUser != null ? pkgSetting.sharedUser.name : "<nothing>")
-                        + " to "
-                        + (sharedUser != null ? sharedUser.name : "<nothing>")
-                        + "; replacing with new");
-                pkgSetting = null;
-            } else {
-                // If what we are scanning is a system (and possibly privileged) package,
-                // then make it so, regardless of whether it was previously installed only
-                // in the data partition.
-                pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
-                pkgSetting.pkgPrivateFlags |=
-                        pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-            }
-        }
-        if (pkgSetting != null) {
+    static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
+            PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
+            File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
+            String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags,
+            UserHandle installUser, boolean allowInstall, String parentPkgName,
+            List<String> childPkgNames, UserManagerService userManager) {
+        final PackageSetting pkgSetting;
+        if (originalPkg != null) {
+            if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
+                    + pkgName + " is adopting original package " + originalPkg.name);
+            pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
+            pkgSetting.childPackageNames =
+                    (childPkgNames != null) ? new ArrayList<>(childPkgNames) : null;
+            pkgSetting.codePath = codePath;
+            pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
+            pkgSetting.origPackage = originalPkg;
+            pkgSetting.parentPackageName = parentPkgName;
+            pkgSetting.pkgFlags = pkgFlags;
+            pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
             pkgSetting.primaryCpuAbiString = primaryCpuAbi;
+            pkgSetting.resourcePath = resourcePath;
             pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
-            if (childPkgNames != null) {
-                pkgSetting.childPackageNames = new ArrayList<>(childPkgNames);
-            }
-
-            if (!pkgSetting.codePath.equals(codePath)) {
-                // Check to see if its a disabled system app
-                if ((pkgSetting.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    // This is an updated system app with versions in both system
-                    // and data partition. Just let the most recent version
-                    // take precedence.
-                    Slog.w(PackageManagerService.TAG,
-                            "Trying to update system app code path from "
-                            + pkgSetting.codePathString + " to " + codePath.toString());
-                } else {
-                    // Just a change in the code path is not an issue, but
-                    // let's log a message about it.
-                    Slog.i(PackageManagerService.TAG,
-                            "Package " + pkgName + " codePath changed from "
-                            + pkgSetting.codePath + " to " + codePath
-                            + "; Retaining data and using new");
-
-                    // The owner user's installed flag is set false
-                    // when the application was installed by other user
-                    // and the installed flag is not updated
-                    // when the application is appended as system app later.
-                    if ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
-                            && disabledPkg == null) {
-                        List<UserInfo> allUserInfos = getAllUsers(userManager);
-                        if (allUserInfos != null) {
-                            for (UserInfo userInfo : allUserInfos) {
-                                pkgSetting.setInstalled(true, userInfo.id);
-                            }
-                        }
-                    }
-
-                    /*
-                     * Since we've changed paths, we need to prefer the new
-                     * native library path over the one stored in the
-                     * package settings since we might have moved from
-                     * internal to external storage or vice versa.
-                     */
-                    pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
-                }
-            }
-        }
-        // TODO: split package setting creation logic out; invoke where getPackageLPw() is
-        // called from
-        if (pkgSetting == null) {
-            if (originalPkg != null) {
-                // We are consuming the data from an existing package.
-                pkgSetting = new PackageSetting(originalPkg.name, pkgName, codePath, resourcePath,
-                        legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
-                        null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
-                        parentPkgName, childPkgNames, 0 /*sharedUserId*/);
-                if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
-                        + pkgName + " is adopting original package " + originalPkg.name);
-                // Note that we will retain the new package's signature so
-                // that we can keep its data.
-                PackageSignatures s = pkgSetting.signatures;
-                pkgSetting.copyFrom(originalPkg);
-                pkgSetting.signatures = s;
-                pkgSetting.sharedUser = originalPkg.sharedUser;
-                pkgSetting.appId = originalPkg.appId;
-                pkgSetting.origPackage = originalPkg;
-                pkgSetting.getPermissionsState().copyFrom(originalPkg.getPermissionsState());
-                pkgName = originalPkg.name;
-                // Update new package state.
-                pkgSetting.setTimeStamp(codePath.lastModified());
-            } else {
-                pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
-                        legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
-                        null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
-                        parentPkgName, childPkgNames, 0 /*sharedUserId*/);
-                pkgSetting.setTimeStamp(codePath.lastModified());
-                pkgSetting.sharedUser = sharedUser;
-                // If this is not a system app, it starts out stopped.
-                if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
-                    if (DEBUG_STOPPED) {
-                        RuntimeException e = new RuntimeException("here");
-                        e.fillInStackTrace();
-                        Slog.i(PackageManagerService.TAG, "Stopping package " + pkgName, e);
-                    }
-                    List<UserInfo> users = getAllUsers(userManager);
-                    final int installUserId = installUser != null ? installUser.getIdentifier() : 0;
-                    if (users != null && allowInstall) {
-                        for (UserInfo user : users) {
-                            // By default we consider this app to be installed
-                            // for the user if no user has been specified (which
-                            // means to leave it at its original value, and the
-                            // original default value is true), or we are being
-                            // asked to install for all users, or this is the
-                            // user we are installing for.
-                            final boolean installed = installUser == null
-                                    || (installUserId == UserHandle.USER_ALL
-                                        && !isAdbInstallDisallowed(userManager, user.id))
-                                    || installUserId == user.id;
-                            pkgSetting.setUserState(user.id, 0, COMPONENT_ENABLED_STATE_DEFAULT,
-                                    installed,
-                                    true, // stopped,
-                                    true, // notLaunched
-                                    false, // hidden
-                                    false, // suspended
-                                    null, null, null,
-                                    false, // blockUninstall
-                                    INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
-                        }
-                    }
-                }
-                if (sharedUser != null) {
-                    pkgSetting.appId = sharedUser.userId;
-                } else {
-                    // Clone the setting here for disabled system packages
-                    if (disabledPkg != null) {
-                        // For disabled packages a new setting is created
-                        // from the existing user id. This still has to be
-                        // added to list of user id's
-                        // Copy signatures from previous setting
-                        if (disabledPkg.signatures.mSignatures != null) {
-                            pkgSetting.signatures.mSignatures =
-                                    disabledPkg.signatures.mSignatures.clone();
-                        }
-                        pkgSetting.appId = disabledPkg.appId;
-                        // Clone permissions
-                        pkgSetting.getPermissionsState().copyFrom(
-                                disabledPkg.getPermissionsState());
-                        // Clone component info
-                        List<UserInfo> users = getAllUsers(userManager);
-                        if (users != null) {
-                            for (UserInfo user : users) {
-                                int userId = user.id;
-                                pkgSetting.setDisabledComponentsCopy(
-                                        disabledPkg.getDisabledComponents(userId), userId);
-                                pkgSetting.setEnabledComponentsCopy(
-                                        disabledPkg.getEnabledComponents(userId), userId);
-                            }
-                        }
-                    }
-                }
-            }
+            // NOTE: Create a deeper copy of the package signatures so we don't
+            // overwrite the signatures in the original package setting.
+            pkgSetting.signatures = new PackageSignatures();
+            pkgSetting.versionCode = versionCode;
+            // Update new package state.
+            pkgSetting.setTimeStamp(codePath.lastModified());
         } else {
-            if (installUser != null && allowInstall) {
-                // The caller has explicitly specified the user they want this
-                // package installed for, and the package already exists.
-                // Make sure it conforms to the new request.
+            pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
+                    legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
+                    null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
+                    parentPkgName, childPkgNames, 0 /*sharedUserId*/);
+            pkgSetting.setTimeStamp(codePath.lastModified());
+            pkgSetting.sharedUser = sharedUser;
+            // If this is not a system app, it starts out stopped.
+            if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+                if (DEBUG_STOPPED) {
+                    RuntimeException e = new RuntimeException("here");
+                    e.fillInStackTrace();
+                    Slog.i(PackageManagerService.TAG, "Stopping package " + pkgName, e);
+                }
                 List<UserInfo> users = getAllUsers(userManager);
-                if (users != null) {
+                final int installUserId = installUser != null ? installUser.getIdentifier() : 0;
+                if (users != null && allowInstall) {
                     for (UserInfo user : users) {
-                        if ((installUser.getIdentifier() == UserHandle.USER_ALL
+                        // By default we consider this app to be installed
+                        // for the user if no user has been specified (which
+                        // means to leave it at its original value, and the
+                        // original default value is true), or we are being
+                        // asked to install for all users, or this is the
+                        // user we are installing for.
+                        final boolean installed = installUser == null
+                                || (installUserId == UserHandle.USER_ALL
                                     && !isAdbInstallDisallowed(userManager, user.id))
-                                || installUser.getIdentifier() == user.id) {
-                            boolean installed = pkgSetting.getInstalled(user.id);
-                            if (!installed) {
-                                pkgSetting.setInstalled(true, user.id);
-                            }
+                                || installUserId == user.id;
+                        pkgSetting.setUserState(user.id, 0, COMPONENT_ENABLED_STATE_DEFAULT,
+                                installed,
+                                true, // stopped,
+                                true, // notLaunched
+                                false, // hidden
+                                false, // suspended
+                                null, null, null,
+                                false, // blockUninstall
+                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+                    }
+                }
+            }
+            if (sharedUser != null) {
+                pkgSetting.appId = sharedUser.userId;
+            } else {
+                // Clone the setting here for disabled system packages
+                if (disabledPkg != null) {
+                    // For disabled packages a new setting is created
+                    // from the existing user id. This still has to be
+                    // added to list of user id's
+                    // Copy signatures from previous setting
+                    pkgSetting.signatures = new PackageSignatures(disabledPkg.signatures);
+                    pkgSetting.appId = disabledPkg.appId;
+                    // Clone permissions
+                    pkgSetting.getPermissionsState().copyFrom(disabledPkg.getPermissionsState());
+                    // Clone component info
+                    List<UserInfo> users = getAllUsers(userManager);
+                    if (users != null) {
+                        for (UserInfo user : users) {
+                            final int userId = user.id;
+                            pkgSetting.setDisabledComponentsCopy(
+                                    disabledPkg.getDisabledComponents(userId), userId);
+                            pkgSetting.setEnabledComponentsCopy(
+                                    disabledPkg.getEnabledComponents(userId), userId);
                         }
                     }
                 }
@@ -966,6 +777,124 @@
         return pkgSetting;
     }
 
+    /**
+     * Updates the given package setting using the provided information.
+     * <p>
+     * WARNING: The provided PackageSetting object may be mutated.
+     */
+    static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
+            @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
+            @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
+            @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi,
+            int pkgFlags, int pkgPrivateFlags, @Nullable List<String> childPkgNames,
+            @NonNull UserManagerService userManager) throws PackageManagerException {
+        final String pkgName = pkgSetting.name;
+        if (pkgSetting.sharedUser != sharedUser) {
+            PackageManagerService.reportSettingsProblem(Log.WARN,
+                    "Package " + pkgName + " shared user changed from "
+                    + (pkgSetting.sharedUser != null ? pkgSetting.sharedUser.name : "<nothing>")
+                    + " to " + (sharedUser != null ? sharedUser.name : "<nothing>"));
+            throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                    "Updating application package " + pkgName + " failed");
+        }
+
+        if (!pkgSetting.codePath.equals(codePath)) {
+            // Check to see if its a disabled system app
+            if ((pkgSetting.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                // This is an updated system app with versions in both system
+                // and data partition. Just let the most recent version
+                // take precedence.
+                Slog.w(PackageManagerService.TAG,
+                        "Trying to update system app code path from "
+                        + pkgSetting.codePathString + " to " + codePath.toString());
+            } else {
+                // Just a change in the code path is not an issue, but
+                // let's log a message about it.
+                Slog.i(PackageManagerService.TAG,
+                        "Package " + pkgName + " codePath changed from "
+                        + pkgSetting.codePath + " to " + codePath
+                        + "; Retaining data and using new");
+
+                // The owner user's installed flag is set false
+                // when the application was installed by other user
+                // and the installed flag is not updated
+                // when the application is appended as system app later.
+                if ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
+                        && disabledPkg == null) {
+                    List<UserInfo> allUserInfos = getAllUsers(userManager);
+                    if (allUserInfos != null) {
+                        for (UserInfo userInfo : allUserInfos) {
+                            pkgSetting.setInstalled(true, userInfo.id);
+                        }
+                    }
+                }
+
+                /*
+                 * Since we've changed paths, we need to prefer the new
+                 * native library path over the one stored in the
+                 * package settings since we might have moved from
+                 * internal to external storage or vice versa.
+                 */
+                pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
+            }
+        }
+        // If what we are scanning is a system (and possibly privileged) package,
+        // then make it so, regardless of whether it was previously installed only
+        // in the data partition.
+        pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
+        pkgSetting.pkgPrivateFlags |=
+                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+        pkgSetting.primaryCpuAbiString = primaryCpuAbi;
+        pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
+        if (childPkgNames != null) {
+            pkgSetting.childPackageNames = new ArrayList<>(childPkgNames);
+        }
+    }
+
+    /**
+     * Registers a user ID with the system. Potentially allocates a new user ID.
+     * @throws PackageManagerException If a user ID could not be allocated.
+     */
+    void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
+        if (p.appId == 0) {
+            // Assign new user ID
+            p.appId = newUserIdLPw(p);
+        } else {
+            // Add new setting to list of user IDs
+            addUserIdLPw(p.appId, p, p.name);
+        }
+        if (p.appId < 0) {
+            PackageManagerService.reportSettingsProblem(Log.WARN,
+                    "Package " + p.name + " could not be assigned a valid uid");
+            throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+                    "Creating application package " + p.name + " failed");
+        }
+    }
+
+    /**
+     * Writes per-user package restrictions if the user state has changed. If the user
+     * state has not changed, this does nothing.
+     */
+    void writeUserRestrictions(PackageSetting newPackage, PackageSetting oldPackage) {
+        // package doesn't exist; do nothing
+        if (peekPackageLPr(newPackage.name) == null) {
+            return;
+        }
+        // no users defined; do nothing
+        final List<UserInfo> allUsers = getAllUsers(UserManagerService.getInstance());
+        if (allUsers == null) {
+            return;
+        }
+        for (UserInfo user : allUsers) {
+            final PackageUserState oldUserState = oldPackage == null
+                    ? PackageSettingBase.DEFAULT_USER_STATE
+                    : oldPackage.readUserState(user.id);
+            if (!oldUserState.equals(newPackage.readUserState(user.id))) {
+                writePackageRestrictionsLPr(user.id);
+            }
+        }
+    }
+
     static boolean isAdbInstallDisallowed(UserManagerService userManager, int userId) {
         return userManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES,
                 userId);
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index df51923..2af1bcb 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -151,6 +152,16 @@
         return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
     }
 
+    public void ensureVersionInfo() {
+        final PackageInfo pi = mShortcutUser.mService.getPackageInfoWithSignatures(
+                getPackageName(), getPackageUserId());
+        if (pi == null) {
+            Slog.w(TAG, "Package not found: " + getPackageName());
+            return;
+        }
+        getPackageInfo().updateVersionInfo(pi);
+    }
+
     /**
      * Persist.
      */
@@ -202,7 +213,7 @@
                 fromBackup ? ownerUserId
                 : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
 
-        final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, launcherUserId,
+        final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, ownerUserId,
                 launcherPackageName, launcherUserId);
 
         ArraySet<String> ids = null;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1acc955..d558b07 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -23,7 +23,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
-import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
@@ -145,30 +144,6 @@
         return mPackageUid;
     }
 
-    /**
-     * Called when a shortcut is about to be published.  At this point we know the publisher
-     * package
-     * exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
-     * we do some initialization for the package.
-     */
-    private void ensurePackageVersionInfo() {
-        // Make sure we have the version code for the app.  We need the version code in
-        // handlePackageUpdated().
-        if (getPackageInfo().getVersionCode() < 0) {
-            final ShortcutService s = mShortcutUser.mService;
-
-            final PackageInfo pi = s.getPackageInfo(getPackageName(), getOwnerUserId());
-            if (pi != null) {
-                if (ShortcutService.DEBUG) {
-                    Slog.d(TAG, String.format("Package %s version = %d", getPackageName(),
-                            pi.versionCode));
-                }
-                getPackageInfo().updateVersionInfo(pi);
-                s.scheduleSaveUser(getOwnerUserId());
-            }
-        }
-    }
-
     @Nullable
     public Resources getPackageResources() {
         return mShortcutUser.mService.injectGetResourcesForApplicationAsUser(
@@ -251,8 +226,6 @@
         Preconditions.checkArgument(newShortcut.isEnabled(),
                 "add/setDynamicShortcuts() cannot publish disabled shortcuts");
 
-        ensurePackageVersionInfo();
-
         newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
 
         final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index e7b66fc..4de15de 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -20,6 +20,7 @@
 import android.content.pm.PackageInfo;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.backup.BackupUtils;
 
 import libcore.io.Base64;
@@ -89,6 +90,7 @@
         return mLastUpdateTime;
     }
 
+    /** Set {@link #mVersionCode} and {@link #mLastUpdateTime} from a {@link PackageInfo}. */
     public void updateVersionInfo(@NonNull PackageInfo pi) {
         if (pi != null) {
             mVersionCode = pi.versionCode;
@@ -119,7 +121,8 @@
         return true;
     }
 
-    public static ShortcutPackageInfo generateForInstalledPackage(
+    @VisibleForTesting
+    public static ShortcutPackageInfo generateForInstalledPackageForTest(
             ShortcutService s, String packageName, @UserIdInt int packageUserId) {
         final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
         if (pi.signatures == null || pi.signatures.length == 0) {
@@ -132,7 +135,7 @@
         return ret;
     }
 
-    public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
+    public void refreshSignature(ShortcutService s, ShortcutPackageItem pkg) {
         if (mIsShadow) {
             s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName()
                     + ", user=" + pkg.getOwnerUserId());
@@ -145,8 +148,6 @@
             Slog.w(TAG, "Package not found: " + pkg.getPackageName());
             return;
         }
-        mVersionCode = pi.versionCode;
-        mLastUpdateTime = pi.lastUpdateTime;
         mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
     }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 1780058..1f195a7 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -65,8 +65,7 @@
     /**
      * ID of the user who actually has this package running on.  For {@link ShortcutPackage},
      * this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and
-     * {@link #getOwnerUserId} is of a work profile, then this ID could be the user who owns the
-     * profile.
+     * {@link #getOwnerUserId} is of work profile, then this ID is of the primary user.
      */
     public int getPackageUserId() {
         return mPackageUserId;
@@ -86,12 +85,12 @@
         return mPackageInfo;
     }
 
-    public void refreshPackageInfoAndSave() {
+    public void refreshPackageSignatureAndSave() {
         if (mPackageInfo.isShadow()) {
             return; // Don't refresh for shadow user.
         }
         final ShortcutService s = mShortcutUser.mService;
-        mPackageInfo.refresh(s, this);
+        mPackageInfo.refreshSignature(s, this);
         s.scheduleSaveUser(getOwnerUserId());
     }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2c61f75..13f558e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1158,7 +1158,10 @@
         }
     }
 
-    /** Return the per-user per-package state. */
+    /**
+     * Return the per-user per-package state.  If the caller is a publisher, use
+     * {@link #getPackageShortcutsForPublisherLocked} instead.
+     */
     @GuardedBy("mLock")
     @NonNull
     ShortcutPackage getPackageShortcutsLocked(
@@ -1166,6 +1169,16 @@
         return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
     }
 
+    /** Return the per-user per-package state.  Use this when the caller is a publisher. */
+    @GuardedBy("mLock")
+    @NonNull
+    ShortcutPackage getPackageShortcutsForPublisherLocked(
+            @NonNull String packageName, @UserIdInt int userId) {
+        final ShortcutPackage ret = getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
+        ret.getUser().onCalledByPublisher(packageName);
+        return ret;
+    }
+
     @GuardedBy("mLock")
     @NonNull
     ShortcutLauncher getLauncherShortcutsLocked(
@@ -1634,8 +1647,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
 
@@ -1686,8 +1698,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
 
@@ -1767,8 +1778,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
 
@@ -1817,8 +1827,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
 
@@ -1847,8 +1856,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
 
@@ -1870,8 +1878,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
 
@@ -1895,8 +1902,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             ps.deleteAllDynamicShortcuts();
         }
         packageShortcutsChanged(packageName, userId);
@@ -1951,8 +1957,7 @@
 
         final ArrayList<ShortcutInfo> ret = new ArrayList<>();
 
-        final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-        ps.getUser().onCalledByPublisher(packageName);
+        final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
         ps.findAll(ret, query, cloneFlags);
 
         return new ParceledListSlice<>(ret);
@@ -1973,8 +1978,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             return mMaxUpdatesPerInterval - ps.getApiCallCount();
         }
     }
@@ -2013,8 +2017,7 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
-            ps.getUser().onCalledByPublisher(packageName);
+            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
 
             if (ps.findShortcutById(shortcutId) == null) {
                 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
@@ -3151,9 +3154,19 @@
                 return null;
             }
 
-            user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave());
+            // Update the signatures for all packages.
+            user.forAllPackageItems(spi -> spi.refreshPackageSignatureAndSave());
 
-            // Then save.
+            // Set the version code for the launchers.
+            // We shouldn't do this for publisher packages, because we don't want to update the
+            // version code without rescanning the manifest.
+            user.forAllLaunchers(launcher -> launcher.ensureVersionInfo());
+
+            // Save to the filesystem.
+            scheduleSaveUser(userId);
+            saveDirtyInfo();
+
+            // Then create the backup payload.
             final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
             try {
                 saveUserInternalLocked(userId, os, /* forBackup */ true);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5176c06..c1cb032 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -113,6 +113,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -130,6 +131,8 @@
     private static final String LOG_TAG = "UserManagerService";
     static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
     private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
+    // Can be used for manual testing of id recycling
+    private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE
 
     private static final String TAG_NAME = "name";
     private static final String TAG_ACCOUNT = "account";
@@ -183,9 +186,15 @@
             | UserInfo.FLAG_GUEST
             | UserInfo.FLAG_DEMO;
 
-    private static final int MIN_USER_ID = 10;
+    @VisibleForTesting
+    static final int MIN_USER_ID = 10;
     // We need to keep process uid within Integer.MAX_VALUE.
-    private static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+    @VisibleForTesting
+    static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+
+    // Max size of the queue of recently removed users
+    @VisibleForTesting
+    static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;
 
     private static final int USER_VERSION = 6;
 
@@ -312,10 +321,17 @@
     /**
      * Set of user IDs being actively removed. Removed IDs linger in this set
      * for several seconds to work around a VFS caching issue.
+     * Use {@link #addRemovingUserIdLocked(int)} to add elements to this array
      */
     @GuardedBy("mUsersLock")
     private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
 
+    /**
+     * Queue of recently removed userIds. Used for recycling of userIds
+     */
+    @GuardedBy("mUsersLock")
+    private final LinkedList<Integer> mRecentlyRemovedIds = new LinkedList<>();
+
     @GuardedBy("mUsersLock")
     private int[] mUserIds;
     @GuardedBy("mPackagesLock")
@@ -401,9 +417,10 @@
         }
     }
 
+    // TODO b/28848102 Add support for test dependencies injection
     @VisibleForTesting
-    UserManagerService(File dataDir) {
-        this(null, null, new Object(), dataDir);
+    UserManagerService(Context context) {
+        this(context, null, new Object(), context.getCacheDir());
     }
 
     /**
@@ -472,7 +489,7 @@
                 UserInfo ui = mUsers.valueAt(i).info;
                 if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
                     partials.add(ui);
-                    mRemovingUserIds.append(ui.id, true);
+                    addRemovingUserIdLocked(ui.id);
                     ui.partial = true;
                 }
             }
@@ -1791,11 +1808,7 @@
         }
         // Create the system user
         UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags);
-        UserData userData = new UserData();
-        userData.info = system;
-        synchronized (mUsersLock) {
-            mUsers.put(system.id, userData);
-        }
+        UserData userData = putUserInfo(system);
         mNextSerialNumber = MIN_USER_ID;
         mUserVersion = USER_VERSION;
 
@@ -2335,6 +2348,23 @@
         return userInfo;
     }
 
+    @VisibleForTesting
+    UserData putUserInfo(UserInfo userInfo) {
+        final UserData userData = new UserData();
+        userData.info = userInfo;
+        synchronized (mUsers) {
+            mUsers.put(userInfo.id, userData);
+        }
+        return userData;
+    }
+
+    @VisibleForTesting
+    void removeUserInfo(int userId) {
+        synchronized (mUsers) {
+            mUsers.remove(userId);
+        }
+    }
+
     /**
      * @hide
      */
@@ -2451,10 +2481,7 @@
                         return false;
                     }
 
-                    // We remember deleted user IDs to prevent them from being
-                    // reused during the current boot; they can still be reused
-                    // after a reboot.
-                    mRemovingUserIds.put(userHandle, true);
+                    addRemovingUserIdLocked(userHandle);
                 }
 
                 try {
@@ -2501,6 +2528,19 @@
         }
     }
 
+    @VisibleForTesting
+    void addRemovingUserIdLocked(int userId) {
+        // We remember deleted user IDs to prevent them from being
+        // reused during the current boot; they can still be reused
+        // after a reboot or recycling of userIds.
+        mRemovingUserIds.put(userId, true);
+        mRecentlyRemovedIds.add(userId);
+        // Keep LRU queue of recently removed IDs for recycling
+        if (mRecentlyRemovedIds.size() > MAX_RECENTLY_REMOVED_IDS_SIZE) {
+            mRecentlyRemovedIds.removeFirst();
+        }
+    }
+
     void finishRemoveUser(final int userHandle) {
         if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
         // Let other services shutdown any activity and clean up their state before completely
@@ -2586,6 +2626,11 @@
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
         userFile.delete();
         updateUserIds();
+        if (RELEASE_DELETED_USER_ID) {
+            synchronized (mUsers) {
+                mRemovingUserIds.delete(userHandle);
+            }
+        }
     }
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
@@ -2966,20 +3011,39 @@
 
     /**
      * Returns the next available user id, filling in any holes in the ids.
-     * TODO: May not be a good idea to recycle ids, in case it results in confusion
-     * for data and battery stats collection, or unexpected cross-talk.
      */
-    private int getNextAvailableId() {
+    @VisibleForTesting
+    int getNextAvailableId() {
+        int nextId;
         synchronized (mUsersLock) {
-            int i = MIN_USER_ID;
-            while (i < MAX_USER_ID) {
-                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
-                    return i;
+            nextId = scanNextAvailableIdLocked();
+            if (nextId >= 0) {
+                return nextId;
+            }
+            // All ids up to MAX_USER_ID were used. Remove all mRemovingUserIds,
+            // except most recently removed
+            if (mRemovingUserIds.size() > 0) {
+                Slog.i(LOG_TAG, "All available IDs are used. Recycling LRU ids.");
+                mRemovingUserIds.clear();
+                for (Integer recentlyRemovedId : mRecentlyRemovedIds) {
+                    mRemovingUserIds.put(recentlyRemovedId, true);
                 }
-                i++;
+                nextId = scanNextAvailableIdLocked();
             }
         }
-        throw new IllegalStateException("No user id available!");
+        if (nextId < 0) {
+            throw new IllegalStateException("No user id available!");
+        }
+        return nextId;
+    }
+
+    private int scanNextAvailableIdLocked() {
+        for (int i = MIN_USER_ID; i < MAX_USER_ID; i++) {
+            if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
+                return i;
+            }
+        }
+        return -1;
     }
 
     private String packageToRestrictionsFileName(String packageName) {
@@ -3284,6 +3348,10 @@
             synchronized (mUsersLock) {
                 pw.println();
                 pw.println("  Device managed: " + mIsDeviceManaged);
+                if (mRemovingUserIds.size() > 0) {
+                    pw.println();
+                    pw.println("  Recently removed userIds: " + mRecentlyRemovedIds);
+                }
             }
             synchronized (mUserStates) {
                 pw.println("  Started users state: " + mUserStates);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cfec1ba..060937a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -647,6 +647,9 @@
     // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
     int mIncallPowerBehavior;
 
+    // Behavior of Back button while in-call and screen on
+    int mIncallBackBehavior;
+
     Display mDisplay;
 
     private int mDisplayRotation;
@@ -838,6 +841,9 @@
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
@@ -1060,7 +1066,7 @@
             if (mBackKeyPressCounter <= PANIC_PRESS_BACK_COUNT) {
                 // This could be a multi-press.  Wait a little bit longer to confirm.
                 Message msg = mHandler.obtainMessage(MSG_BACK_DELAYED_PRESS,
-                        mBackKeyPressCounter, 0, eventTime);
+                    mBackKeyPressCounter, 0, eventTime);
                 msg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
             }
@@ -1069,6 +1075,27 @@
         // Reset back long press state
         cancelPendingBackKeyAction();
 
+        if (mHasFeatureWatch) {
+            TelecomManager telecomManager = getTelecommService();
+
+            if (telecomManager != null) {
+                if (telecomManager.isRinging()) {
+                    // Pressing back while there's a ringing incoming
+                    // call should silence the ringer.
+                    telecomManager.silenceRinger();
+
+                    // It should not prevent navigating away
+                    return false;
+                } else if (
+                    (mIncallBackBehavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0
+                        && telecomManager.isInCall()) {
+                    // Otherwise, if "Back button ends call" is enabled,
+                    // the Back button will hang up any current active call.
+                    return telecomManager.endCall();
+                }
+            }
+        }
+
         return handled;
     }
 
@@ -1905,6 +1932,7 @@
     public void setInitialDisplaySize(Display display, int width, int height, int density) {
         // This method might be called before the policy has been fully initialized
         // or for other displays we don't care about.
+        // TODO(multi-display): Define policy for secondary displays.
         if (mContext == null || display.getDisplayId() != Display.DEFAULT_DISPLAY) {
             return;
         }
@@ -2000,6 +2028,7 @@
 
     @Override
     public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
+        // TODO(multi-display): Define policy for secondary displays.
         if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
             mOverscanLeft = left;
             mOverscanTop = top;
@@ -2020,6 +2049,10 @@
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
                     UserHandle.USER_CURRENT);
+            mIncallBackBehavior = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
+                    Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_DEFAULT,
+                    UserHandle.USER_CURRENT);
 
             // Configure wake gesture.
             boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -2397,6 +2430,7 @@
 
     @Override
     public void onConfigurationChanged() {
+        // TODO(multi-display): Define policy for secondary displays.
         final Resources res = mContext.getResources();
 
         mStatusBarHeight =
@@ -5615,7 +5649,14 @@
 
                 @Override
                 public void onServiceDisconnected(ComponentName name) {
-                    notifyScreenshotError();
+                    synchronized (mScreenshotLock) {
+                        if (mScreenshotConnection != null) {
+                            mContext.unbindService(mScreenshotConnection);
+                            mScreenshotConnection = null;
+                            mHandler.removeCallbacks(mScreenshotTimeout);
+                            notifyScreenshotError();
+                        }
+                    }
                 }
             };
             if (mContext.bindServiceAsUser(serviceIntent, conn,
@@ -7849,7 +7890,7 @@
         int delta = newRotation - oldRotation;
         if (delta < 0) delta += 4;
         // Likewise we don't rotate seamlessly for 180 degree rotations
-        // in this case the surfaces never resize, and our logic to 
+        // in this case the surfaces never resize, and our logic to
         // revert the transformations on size change will fail. We could
         // fix this in the future with the "tagged" frames idea.
         if (delta == Surface.ROTATION_180) {
@@ -8046,6 +8087,7 @@
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
         pw.print(prefix); pw.print("mEndcallBehavior="); pw.print(mEndcallBehavior);
                 pw.print(" mIncallPowerBehavior="); pw.print(mIncallPowerBehavior);
+                pw.print(" mIncallBackBehavior="); pw.print(mIncallBackBehavior);
                 pw.print(" mLongPressOnHomeBehavior="); pw.println(mLongPressOnHomeBehavior);
         pw.print(prefix); pw.print("mLandscapeRotation="); pw.print(mLandscapeRotation);
                 pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a2b86e3..3f58c95 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -650,10 +650,12 @@
                     false, mSettingsObserver, UserHandle.USER_ALL);
             IVrManager vrManager =
                     (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
-            try {
-                vrManager.registerListener(mVrStateCallbacks);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+            if (vrManager != null) {
+                try {
+                    vrManager.registerListener(mVrStateCallbacks);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+                }
             }
             // Go.
             readConfigurationLocked();
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index af68137..305e47f 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -146,7 +146,7 @@
 
         this.mSkipFirstFrame = skipFirstFrame;
 
-        if (!mAppToken.appFullscreen) {
+        if (!mAppToken.fillsParent()) {
             anim.setBackgroundColor(0);
         }
         if (mClearProlongedAnimation) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d7e7f81..4d12ba9 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,10 +17,13 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -40,7 +43,6 @@
 import com.android.server.wm.WindowManagerService.H;
 
 import android.annotation.NonNull;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -64,7 +66,7 @@
  * Version of WindowToken that is specifically for a particular application (or
  * really activity) that is displaying windows.
  */
-class AppWindowToken extends WindowToken {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
 
     // Non-null only for application tokens.
@@ -74,10 +76,10 @@
 
     final boolean voiceInteraction;
 
+    // TODO: Use getParent instead?
     Task mTask;
-    // TODO: Have a fillParent variable in WindowContainer to this?
-    boolean appFullscreen;
-    int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    /** @see WindowContainer#fillsParent() */
+    private boolean mFillsParent;
     boolean layoutConfigChanges;
     boolean showForAllUsers;
     int targetSdk;
@@ -190,7 +192,7 @@
         mReportedVisibilityResults.reset();
 
         for (int i = 0; i < count; i++) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             win.updateReportedVisibility(mReportedVisibilityResults);
         }
 
@@ -270,7 +272,7 @@
 
             final int windowsCount = mChildren.size();
             for (int i = 0; i < windowsCount; i++) {
-                final WindowState win = (WindowState) mChildren.get(i);
+                final WindowState win = mChildren.get(i);
                 changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
             }
 
@@ -306,7 +308,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
-            if (((WindowState) mChildren.get(i)).isWindowAnimationSet()) {
+            if ((mChildren.get(i)).isWindowAnimationSet()) {
                 delayed = true;
             }
         }
@@ -323,6 +325,8 @@
                 // The token is not closing nor opening, so even if there is an animation set, that
                 // doesn't mean that it goes through the normal app transition cycle so we have
                 // to inform the docked controller about visibility change.
+                // TODO(multi-display): notify docked divider on all displays where visibility was
+                // affected.
                 mService.getDefaultDisplayContentLocked().getDockedDividerController()
                         .notifyAppVisibilityChanged();
             }
@@ -336,7 +340,7 @@
         int j = mChildren.size();
         while (j > 0) {
             j--;
-            final WindowState win = (WindowState) mChildren.get(j);
+            final WindowState win = mChildren.get(j);
             final int type = win.mAttrs.type;
             // No need to loop through child window as base application and starting types can't be
             // child windows.
@@ -372,14 +376,23 @@
         mIsExiting = false;
         removeAllWindows();
         if (mTask != null) {
-            mTask.detachChild(this);
+            mTask.mStack.mExitingAppTokens.remove(this);
+            mTask.removeChild(this);
         }
     }
 
+    @Override
+    boolean checkCompleteDeferredRemoval() {
+        if (mIsExiting) {
+            removeIfPossible();
+        }
+        return super.checkCompleteDeferredRemoval();
+    }
+
     void clearAnimatingFlags() {
         boolean wallpaperMightChange = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             wallpaperMightChange |= win.clearAnimatingFlags();
         }
         if (wallpaperMightChange) {
@@ -403,7 +416,7 @@
     private void destroySurfaces(boolean cleanupOnResume) {
         final DisplayContentList displayList = new DisplayContentList();
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             final boolean destroyed = win.destroySurface(cleanupOnResume, mAppStopped);
 
             if (destroyed) {
@@ -463,7 +476,7 @@
 
     private boolean canRestoreSurfaces() {
         for (int i = mChildren.size() -1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             if (w.canRestoreSurface()) {
                 return true;
             }
@@ -473,7 +486,7 @@
 
     private void clearWasVisibleBeforeClientHidden() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.clearWasVisibleBeforeClientHidden();
         }
     }
@@ -484,7 +497,7 @@
      */
     boolean isAnimatingInvisibleWithSavedSurface() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             if (w.isAnimatingInvisibleWithSavedSurface()) {
                 return true;
             }
@@ -498,7 +511,7 @@
      */
     void stopUsingSavedSurfaceLocked() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.stopUsingSavedSurface();
         }
         destroySurfaces();
@@ -506,7 +519,7 @@
 
     void markSavedSurfaceExiting() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.markSavedSurfaceExiting();
         }
     }
@@ -521,7 +534,7 @@
         int interestingNotDrawn = -1;
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             interestingNotDrawn = w.restoreSavedSurfaceForInterestingWindow();
         }
 
@@ -540,7 +553,7 @@
 
     void destroySavedSurfaces() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             win.destroySavedSurface();
         }
     }
@@ -551,10 +564,7 @@
         allDrawnExcludingSaved = false;
     }
 
-    @Override
-    void removeWindow(WindowState win) {
-        super.removeWindow(win);
-
+    void postWindowRemoveStartingWindowCleanup(WindowState win) {
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
@@ -573,7 +583,7 @@
 
     void removeDeadWindows() {
         for (int winNdx = mChildren.size() - 1; winNdx >= 0; --winNdx) {
-            WindowState win = (WindowState) mChildren.get(winNdx);
+            WindowState win = mChildren.get(winNdx);
             if (win.mAppDied) {
                 if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.w(TAG,
                         "removeDeadWindows: " + win);
@@ -589,7 +599,7 @@
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             // No need to loop through child windows as the answer should be the same as that of the
             // parent window.
-            if (!((WindowState) mChildren.get(i)).mAppDied) {
+            if (!(mChildren.get(i)).mAppDied) {
                 return true;
             }
         }
@@ -601,7 +611,7 @@
                 "Marking app token " + this + " with replacing windows.");
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.setWillReplaceWindow(animate);
         }
         if (animate) {
@@ -618,7 +628,7 @@
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + this
                 + " with replacing child windows.");
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.setWillReplaceChildWindows();
         }
     }
@@ -628,14 +638,14 @@
                 "Resetting app token " + this + " of replacing window marks.");
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.clearWillReplaceWindow();
         }
     }
 
     void requestUpdateWallpaperIfNeeded() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             w.requestUpdateWallpaperIfNeeded();
         }
     }
@@ -676,7 +686,7 @@
 
         boolean gotReplacementWindow = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState candidate = (WindowState) mChildren.get(i);
+            final WindowState candidate = mChildren.get(i);
             gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w);
         }
 
@@ -688,7 +698,7 @@
 
     boolean waitingForReplacement() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState candidate = (WindowState) mChildren.get(i);
+            final WindowState candidate = mChildren.get(i);
             if (candidate.waitingForReplacement()) {
                 return true;
             }
@@ -698,7 +708,7 @@
 
     void onWindowReplacementTimeout() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            ((WindowState) mChildren.get(i)).onWindowReplacementTimeout();
+            (mChildren.get(i)).onWindowReplacementTimeout();
         }
     }
 
@@ -719,13 +729,14 @@
 
         if (mTask.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
             // We didn't call prepareFreezingBounds on the task, so use the current value.
-            final Configuration config = new Configuration(mService.mCurConfiguration);
+            final Configuration config = new Configuration(mService.mGlobalConfiguration);
             config.updateFrom(mTask.mOverrideConfig);
             mFrozenMergedConfig.offer(config);
         } else {
             mFrozenMergedConfig.offer(new Configuration(mTask.mPreparedFrozenMergedConfig));
         }
-        mTask.mPreparedFrozenMergedConfig.setToDefaults();
+        // Calling unset() to make it equal to Configuration.EMPTY.
+        mTask.mPreparedFrozenMergedConfig.unset();
     }
 
     /**
@@ -739,7 +750,7 @@
             mFrozenMergedConfig.remove();
         }
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             win.onUnfreezeBounds();
         }
         mService.mWindowPlacerLocked.performSurfacePlacement();
@@ -789,13 +800,13 @@
 
     void resetJustMovedInStack() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            ((WindowState) mChildren.get(i)).resetJustMovedInStack();
+            (mChildren.get(i)).resetJustMovedInStack();
         }
     }
 
     void notifyMovedInStack() {
         for (int winNdx = mChildren.size() - 1; winNdx >= 0; --winNdx) {
-            final WindowState win = (WindowState) mChildren.get(winNdx);
+            final WindowState win = mChildren.get(winNdx);
             win.notifyMovedInStack();
         }
     }
@@ -804,7 +815,7 @@
         final WindowAnimator windowAnimator = mAppAnimator.mAnimator;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             // Child windows will be on the same display as their parents.
-            if (displayId == ((WindowState) mChildren.get(i)).getDisplayId()) {
+            if (displayId == (mChildren.get(i)).getDisplayId()) {
                 windowAnimator.setPendingLayoutChanges(displayId, changes);
                 if (DEBUG_LAYOUT_REPEATS) {
                     mService.mWindowPlacerLocked.debugLayoutRepeats(
@@ -817,7 +828,7 @@
 
     void removeReplacedWindowIfNeeded(WindowState replacement) {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             if (win.removeReplacedWindowIfNeeded(replacement)) {
                 return;
             }
@@ -831,6 +842,7 @@
         if (!hiddenRequested) {
             if (!mAppAnimator.freezingScreen) {
                 mAppAnimator.freezingScreen = true;
+                mService.registerAppFreezeListener(this);
                 mAppAnimator.lastFreezeDuration = 0;
                 mService.mAppsFreezingScreen++;
                 if (mService.mAppsFreezingScreen == 1) {
@@ -841,7 +853,7 @@
             }
             final int count = mChildren.size();
             for (int i = 0; i < count; i++) {
-                final WindowState w = (WindowState) mChildren.get(i);
+                final WindowState w = mChildren.get(i);
                 w.onStartFreezingScreen();
             }
         }
@@ -855,12 +867,13 @@
         final int count = mChildren.size();
         boolean unfrozeWindows = false;
         for (int i = 0; i < count; i++) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             unfrozeWindows |= w.onStopFreezingScreen();
         }
         if (force || unfrozeWindows) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
             mAppAnimator.freezingScreen = false;
+            mService.unregisterAppFreezeListener(this);
             mAppAnimator.lastFreezeDuration =
                     (int)(SystemClock.elapsedRealtime() - mService.mDisplayFreezeTime);
             mService.mAppsFreezingScreen--;
@@ -874,6 +887,12 @@
         }
     }
 
+    @Override
+    public void onAppFreezeTimeout() {
+        Slog.w(TAG_WM, "Force clearing freeze: " + this);
+        stopFreezingScreen(true, true);
+    }
+
     boolean transferStartingWindow(IBinder transferFrom) {
         final AppWindowToken fromToken = mService.findAppWindowToken(transferFrom);
         if (fromToken == null) {
@@ -911,7 +930,8 @@
             mService.mWindowsChanged = true;
             if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                     "Removing starting " + tStartingWindow + " from " + fromToken);
-            fromToken.removeWindow(tStartingWindow);
+            fromToken.removeChild(tStartingWindow);
+            fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow);
             addWindow(tStartingWindow);
 
             // Propagate other interesting state between the tokens. If the old token is displayed,
@@ -981,25 +1001,137 @@
 
         final int windowsCount = mChildren.size();
         for (int j = 0; j < windowsCount; j++) {
-            ((WindowState) mChildren.get(j)).addWinAnimatorToList(allAppWinAnimators);
+            (mChildren.get(j)).addWinAnimatorToList(allAppWinAnimators);
         }
     }
 
     @Override
+    void onAppTransitionDone() {
+        sendingToBottom = false;
+    }
+
+    /**
+     * We override because this class doesn't want its children affecting its reported orientation
+     * in anyway.
+     */
+    @Override
+    int getOrientation() {
+        if (hidden || hiddenRequested) {
+            return SCREEN_ORIENTATION_UNSET;
+        }
+        return mOrientation;
+    }
+
+    @Override
+    void checkAppWindowsReadyToShow(int displayId) {
+        if (allDrawn == mAppAnimator.allDrawn) {
+            return;
+        }
+
+        mAppAnimator.allDrawn = allDrawn;
+        if (!allDrawn) {
+            return;
+        }
+
+        // The token has now changed state to having all windows shown...  what to do, what to do?
+        if (mAppAnimator.freezingScreen) {
+            mAppAnimator.showAllWindowsLocked();
+            stopFreezingScreen(false, true);
+            if (DEBUG_ORIENTATION) Slog.i(TAG,
+                    "Setting mOrientationChangeComplete=true because wtoken " + this
+                    + " numInteresting=" + numInterestingWindows + " numDrawn=" + numDrawnWindows);
+            // This will set mOrientationChangeComplete and cause a pass through layout.
+            setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
+                    "checkAppWindowsReadyToShow: freezingScreen", displayId);
+        } else {
+            setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow", displayId);
+
+            // We can now show all of the drawn windows!
+            if (!mService.mOpeningApps.contains(this)) {
+                mService.mAnimator.orAnimating(mAppAnimator.showAllWindowsLocked());
+            }
+        }
+    }
+
+    @Override
+    void updateAllDrawn(int displayId) {
+        final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+
+        if (!allDrawn) {
+            final int numInteresting = numInterestingWindows;
+            if (numInteresting > 0 && numDrawnWindows >= numInteresting) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
+                        + " interesting=" + numInteresting + " drawn=" + numDrawnWindows);
+                allDrawn = true;
+                // Force an additional layout pass where
+                // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
+                displayContent.layoutNeeded = true;
+                mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+            }
+        }
+        if (!allDrawnExcludingSaved) {
+            int numInteresting = numInterestingWindowsExcludingSaved;
+            if (numInteresting > 0 && numDrawnWindowsExcludingSaved >= numInteresting) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawnExcludingSaved: " + this
+                        + " interesting=" + numInteresting
+                        + " drawn=" + numDrawnWindowsExcludingSaved);
+                allDrawnExcludingSaved = true;
+                displayContent.layoutNeeded = true;
+                if (isAnimatingInvisibleWithSavedSurface()
+                        && !mService.mFinishedEarlyAnim.contains(this)) {
+                    mService.mFinishedEarlyAnim.add(this);
+                }
+            }
+        }
+    }
+
+    @Override
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        mAppAnimator.wasAnimating = mAppAnimator.animating;
+        if (mAppAnimator.stepAnimationLocked(currentTime, displayId)) {
+            mAppAnimator.animating = true;
+            mService.mAnimator.setAnimating(true);
+            mService.mAnimator.mAppWindowAnimating = true;
+        } else if (mAppAnimator.wasAnimating) {
+            // stopped animating, do one more pass through the layout
+            setAppLayoutChanges(
+                    FINISH_LAYOUT_REDO_WALLPAPER, "appToken " + this + " done", displayId);
+            if (DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating " + this);
+        }
+    }
+
+    @Override
+    int rebuildWindowList(DisplayContent dc, int addIndex) {
+        if (mIsExiting && !waitingForReplacement()) {
+            return addIndex;
+        }
+        return super.rebuildWindowList(dc, addIndex);
+    }
+
+    @Override
     AppWindowToken asAppWindowToken() {
         // I am an app window token!
         return this;
     }
 
     @Override
+    boolean fillsParent() {
+        return mFillsParent;
+    }
+
+    void setFillsParent(boolean fillsParent) {
+        mFillsParent = fillsParent;
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
         if (appToken != null) {
             pw.print(prefix); pw.print("app=true voiceInteraction="); pw.println(voiceInteraction);
         }
         pw.print(prefix); pw.print("task="); pw.println(mTask);
-        pw.print(prefix); pw.print(" appFullscreen="); pw.print(appFullscreen);
-                pw.print(" requestedOrientation="); pw.println(requestedOrientation);
+        pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
+                pw.print(" mOrientation="); pw.println(mOrientation);
         pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
                 pw.print(" clientHidden="); pw.print(clientHidden);
                 pw.print(" reportedDrawn="); pw.print(reportedDrawn);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 052b2f5..b99dda1 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -95,7 +95,7 @@
     }
 
     private void constructSurface(WindowManagerService service) {
-        SurfaceControl.openTransaction();
+        service.openSurfaceTransaction();
         try {
             if (DEBUG_SURFACE_TRACE) {
                 mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
@@ -116,7 +116,7 @@
         } catch (Exception e) {
             Slog.e(TAG_WM, "Exception creating Dim surface", e);
         } finally {
-            SurfaceControl.closeTransaction();
+            service.closeSurfaceTransaction();
         }
     }
 
@@ -227,12 +227,12 @@
         mBounds.set(bounds);
         if (isDimming() && !mLastBounds.equals(bounds)) {
             try {
-                SurfaceControl.openTransaction();
+                mService.openSurfaceTransaction();
                 adjustBounds();
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Failure setting size", e);
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3a3c017..4fc3ab0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -17,30 +17,51 @@
 package com.android.server.wm;
 
 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.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManagerInternal;
+import android.os.Debug;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.IWindow;
 import android.view.Surface;
 import android.view.animation.Animation;
+import com.android.internal.util.FastPrintWriter;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 class DisplayContentList extends ArrayList<DisplayContent> {
 }
@@ -73,7 +94,7 @@
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
 
     Rect mBaseDisplayRect = new Rect();
-    Rect mContentRect = new Rect();
+    private Rect mContentRect = new Rect();
 
     // Accessed directly by all users.
     boolean layoutNeeded;
@@ -95,16 +116,13 @@
     TaskTapPointerEventListener mTapDetector;
 
     /** Detect user tapping outside of current focused stack bounds .*/
-    Region mTouchExcludeRegion = new Region();
+    private Region mTouchExcludeRegion = new Region();
 
     /** Save allocating when calculating rects */
     private final Rect mTmpRect = new Rect();
     private final Rect mTmpRect2 = new Rect();
     private final Region mTmpRegion = new Region();
 
-    /** For gathering Task objects in order. */
-    final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>();
-
     final WindowManagerService mService;
 
     /** Remove this display when animation on it has completed. */
@@ -116,6 +134,14 @@
 
     final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
 
+    /** Used when rebuilding window list to keep track of windows that have been removed. */
+    private WindowState[] mRebuildTmp = new WindowState[20];
+
+    private final TaskForResizePointSearchResult mTmpTaskForResizePointSearchResult =
+            new TaskForResizePointSearchResult();
+    private final GetWindowOnDisplaySearchResult mTmpGetWindowOnDisplaySearchResult =
+            new GetWindowOnDisplaySearchResult();
+
     /**
      * @param display May not be null.
      * @param service You know.
@@ -171,19 +197,6 @@
         return mStacks;
     }
 
-    /**
-     * Retrieve the tasks on this display in stack order from the bottommost TaskStack up.
-     * @return All the Tasks, in order, on this display.
-     */
-    ArrayList<Task> getTasks() {
-        mTmpTaskHistory.clear();
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            mTmpTaskHistory.addAll(mStacks.get(stackNdx).getTasks());
-        }
-        return mTmpTaskHistory;
-    }
-
     TaskStack getHomeStack() {
         if (mHomeStack == null && mDisplayId == Display.DEFAULT_DISPLAY) {
             Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
@@ -201,6 +214,98 @@
         return null;
     }
 
+    /** Callback used to notify about configuration changes. */
+    void onConfigurationChanged(@NonNull List<Integer> changedStackList) {
+        // The display size information is heavily dependent on the resources in the current
+        // configuration, so we need to reconfigure it every time the configuration changes.
+        // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh...
+        mService.reconfigureDisplayLocked(this);
+
+        getDockedDividerController().onConfigurationChanged();
+
+        for (int i = 0; i < mStacks.size(); i++) {
+            final TaskStack stack = mStacks.get(i);
+            if (stack.onConfigurationChanged()) {
+                changedStackList.add(stack.mStackId);
+            }
+        }
+    }
+
+    void checkAppWindowsReadyToShow() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.checkAppWindowsReadyToShow(mDisplayId);
+        }
+    }
+
+    void updateAllDrawn() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.updateAllDrawn(mDisplayId);
+        }
+    }
+
+    void stepAppWindowsAnimation(long currentTime) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.stepAppWindowsAnimation(currentTime, mDisplayId);
+        }
+    }
+
+    void onAppTransitionDone() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.onAppTransitionDone();
+        }
+
+        rebuildAppWindowList();
+    }
+
+    int getOrientation() {
+        // TODO: Most of the logic here can be removed once this class is converted to use
+        // WindowContainer which has an abstract implementation of getOrientation that
+        // should cover this.
+        if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
+                || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+            // Apps and their containers are not allowed to specify an orientation while the docked
+            // or freeform stack is visible...except for the home stack/task if the docked stack is
+            // minimized and it actually set something.
+            if (mHomeStack != null && mHomeStack.isVisible()
+                    && mDividerControllerLocked.isMinimizedDock()) {
+                final int orientation = mHomeStack.getOrientation();
+                if (orientation != SCREEN_ORIENTATION_UNSET) {
+                    return orientation;
+                }
+            }
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            if (!stack.isVisible()) {
+                continue;
+            }
+
+            final int orientation = stack.getOrientation();
+
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                continue;
+            }
+
+            if (orientation != SCREEN_ORIENTATION_UNSET) {
+                if (stack.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                    return orientation;
+                }
+            }
+        }
+
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                "No app is requesting an orientation, return " + mService.mLastOrientation);
+        // The next app has not been requested to be visible, so we keep the current orientation
+        // to prevent freezing/unfreezing the display too early.
+        return mService.mLastOrientation;
+    }
+
     void updateDisplayInfo() {
         mDisplay.getDisplayInfo(mDisplayInfo);
         mDisplay.getMetrics(mDisplayMetrics);
@@ -286,6 +391,19 @@
         mStacks.add(addIndex, stack);
     }
 
+    // TODO: Don't forget to switch to WC.removeChild
+    void detachChild(TaskStack stack) {
+        detachStack(stack);
+        if (stack.detachFromDisplay()) {
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+        if (stack.mStackId == DOCKED_STACK_ID) {
+            mService.getDefaultDisplayContentLocked().mDividerControllerLocked
+                    .notifyDockedStackExistsChanged(false);
+        }
+    }
+
+    // TODO: See about removing this by untangling the use case in WMS.attachStack()
     void detachStack(TaskStack stack) {
         mDimLayerController.removeDimLayerUser(stack);
         mStacks.remove(stack);
@@ -301,27 +419,10 @@
 
     int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            TaskStack stack = mStacks.get(stackNdx);
-            stack.getBounds(mTmpRect);
-            if (!mTmpRect.contains(x, y) || stack.isAdjustedForMinimizedDockedStack()) {
-                continue;
-            }
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                final WindowState win = task.getTopVisibleAppMainWindow();
-                if (win == null) {
-                    continue;
-                }
-                // We need to use the task's dim bounds (which is derived from the visible
-                // bounds of its apps windows) for any touch-related tests. Can't use
-                // the task's original bounds because it might be adjusted to fit the
-                // content frame. For example, the presence of the IME adjusting the
-                // windows frames when the app window is the IME target.
-                task.getDimBounds(mTmpRect);
-                if (mTmpRect.contains(x, y)) {
-                    return task.mTaskId;
-                }
+            final TaskStack stack = mStacks.get(stackNdx);
+            final int taskId = stack.taskIdFromPoint(x, y);
+            if (taskId != -1) {
+                return taskId;
             }
         }
         return -1;
@@ -333,35 +434,16 @@
      */
     Task findTaskForResizePoint(int x, int y) {
         final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
+        mTmpTaskForResizePointSearchResult.reset();
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             TaskStack stack = mStacks.get(stackNdx);
             if (!StackId.isTaskResizeAllowed(stack.mStackId)) {
-                break;
+                return null;
             }
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                if (task.isFullscreen()) {
-                    return null;
-                }
 
-                // We need to use the task's dim bounds (which is derived from the visible
-                // bounds of its apps windows) for any touch-related tests. Can't use
-                // the task's original bounds because it might be adjusted to fit the
-                // content frame. One example is when the task is put to top-left quadrant,
-                // the actual visible area would not start at (0,0) after it's adjusted
-                // for the status bar.
-                task.getDimBounds(mTmpRect);
-                mTmpRect.inset(-delta, -delta);
-                if (mTmpRect.contains(x, y)) {
-                    mTmpRect.inset(delta, delta);
-                    if (!mTmpRect.contains(x, y)) {
-                        return task;
-                    }
-                    // User touched inside the task. No need to look further,
-                    // focus transfer will be handled in ACTION_UP.
-                    return null;
-                }
+            stack.findTaskForResizePoint(x, y, delta, mTmpTaskForResizePointSearchResult);
+            if (mTmpTaskForResizePointSearchResult.searchDone) {
+                return mTmpTaskForResizePointSearchResult.taskForResize;
             }
         }
         return null;
@@ -370,54 +452,16 @@
     void setTouchExcludeRegion(Task focusedTask) {
         mTouchExcludeRegion.set(mBaseDisplayRect);
         final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
-        boolean addBackFocusedTask = false;
+        mTmpRect2.setEmpty();
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            TaskStack stack = mStacks.get(stackNdx);
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                AppWindowToken token = task.getTopVisibleAppToken();
-                if (token == null || !token.isVisible()) {
-                    continue;
-                }
-
-                /**
-                 * Exclusion region is the region that TapDetector doesn't care about.
-                 * Here we want to remove all non-focused tasks from the exclusion region.
-                 * We also remove the outside touch area for resizing for all freeform
-                 * tasks (including the focused).
-                 *
-                 * We save the focused task region once we find it, and add it back at the end.
-                 */
-
-                task.getDimBounds(mTmpRect);
-
-                if (task == focusedTask) {
-                    addBackFocusedTask = true;
-                    mTmpRect2.set(mTmpRect);
-                }
-
-                final boolean isFreeformed = task.inFreeformWorkspace();
-                if (task != focusedTask || isFreeformed) {
-                    if (isFreeformed) {
-                        // If the task is freeformed, enlarge the area to account for outside
-                        // touch area for resize.
-                        mTmpRect.inset(-delta, -delta);
-                        // Intersect with display content rect. If we have system decor (status bar/
-                        // navigation bar), we want to exclude that from the tap detection.
-                        // Otherwise, if the app is partially placed under some system button (eg.
-                        // Recents, Home), pressing that button would cause a full series of
-                        // unwanted transfer focus/resume/pause, before we could go home.
-                        mTmpRect.intersect(mContentRect);
-                    }
-                    mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
-                }
-            }
+            final TaskStack stack = mStacks.get(stackNdx);
+            stack.setTouchExcludeRegion(
+                    focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
         }
         // If we removed the focused task above, add it back and only leave its
         // outside touch area in the exclusion. TapDectector is not interested in
         // any touch inside the focused task itself.
-        if (addBackFocusedTask) {
+        if (!mTmpRect2.isEmpty()) {
             mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
         }
         final WindowState inputMethod = mService.mInputMethodWindow;
@@ -457,6 +501,8 @@
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             mStacks.get(stackNdx).switchUser();
         }
+
+        rebuildAppWindowList();
     }
 
     void resetAnimationBackgroundAnimator() {
@@ -498,32 +544,18 @@
         return false;
     }
 
-    void onCompleteDeferredRemoval() {
-        boolean animating = false;
+    /** Returns true if a removal action is still being deferred. */
+    boolean checkCompleteDeferredRemoval() {
+        boolean stillDeferringRemoval = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mStacks.get(stackNdx);
-            if (stack.isAnimating()) {
-                animating = true;
-            } else {
-                if (stack.mDeferDetach) {
-                    mService.detachStackLocked(this, stack);
-                }
-                final ArrayList<Task> tasks = stack.getTasks();
-                for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                    final Task task = tasks.get(taskNdx);
-                    AppTokenList tokens = task.mAppTokens;
-                    for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                        AppWindowToken wtoken = tokens.get(tokenNdx);
-                        if (wtoken.mIsExiting) {
-                            wtoken.removeIfPossible();
-                        }
-                    }
-                }
-            }
+            stillDeferringRemoval |= stack.checkCompleteDeferredRemoval();
         }
-        if (!animating && mDeferredRemoval) {
+        if (!stillDeferringRemoval && mDeferredRemoval) {
             mService.onDisplayRemoved(mDisplayId);
+            return false;
         }
+        return true;
     }
 
     void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
@@ -617,7 +649,11 @@
 
     @Override
     public String toString() {
-        return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks;
+        return getName() + " stacks=" + mStacks;
+    }
+
+    String getName() {
+        return "Display " + mDisplayId + " info=" + mDisplayInfo;
     }
 
     /**
@@ -637,9 +673,7 @@
         return (stack != null && stack.isVisible(true /* ignoreKeyguard */)) ? stack : null;
     }
 
-    /**
-     * Find the visible, touch-deliverable window under the given point
-     */
+    /** Find the visible, touch-deliverable window under the given point */
     WindowState getTouchableWinAtPointLocked(float xf, float yf) {
         WindowState touchedWin = null;
         final int x = (int) xf;
@@ -715,4 +749,467 @@
             }
         }
     }
+
+    WindowState findFocusedWindow() {
+        final AppWindowToken focusedApp = mService.mFocusedApp;
+
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+
+            if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + i + " = " + win
+                    + ", flags=" + win.mAttrs.flags + ", canReceive=" + win.canReceiveKeys());
+
+            if (!win.canReceiveKeys()) {
+                continue;
+            }
+
+            final AppWindowToken wtoken = win.mAppToken;
+
+            // If this window's application has been removed, just skip it.
+            if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) {
+                if (DEBUG_FOCUS) Slog.v(TAG_WM, "Skipping " + wtoken + " because "
+                        + (wtoken.removed ? "removed" : "sendingToBottom"));
+                continue;
+            }
+
+            if (focusedApp == null) {
+                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp=null"
+                        + " using new focus @ " + i + " = " + win);
+                return win;
+            }
+
+            if (!focusedApp.windowsAreFocusable()) {
+                // Current focused app windows aren't focusable...
+                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp windows not"
+                        + " focusable using new focus @ " + i + " = " + win);
+                return win;
+            }
+
+            // Descend through all of the app tokens and find the first that either matches
+            // win.mAppToken (return win) or mFocusedApp (return null).
+            if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING) {
+                final TaskStack focusedAppStack = focusedApp.mTask.mStack;
+                final TaskStack appStack = wtoken.mTask.mStack;
+
+                // TODO: Use WindowContainer.compareTo() once everything is using WindowContainer
+                if ((focusedAppStack == appStack && focusedApp.compareTo(wtoken) > 0)
+                        || mStacks.indexOf(focusedAppStack) > mStacks.indexOf(appStack)) {
+                    // App stack below focused app stack. No focus for you!!!
+                    if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM,
+                            "findFocusedWindow: Reached focused app=" + focusedApp);
+                    return null;
+                }
+            }
+
+            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ "
+                    + i + " = " + win);
+            return win;
+        }
+
+        if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows.");
+        return null;
+    }
+
+    int addAppWindowToWindowList(final WindowState win) {
+        final IWindow client = win.mClient;
+
+        WindowList tokenWindowList = getTokenWindowsOnDisplay(win.mToken);
+        if (!tokenWindowList.isEmpty()) {
+            return addAppWindowExisting(win, tokenWindowList);
+        }
+
+        // No windows from this token on this display
+        if (mService.localLOGV) Slog.v(TAG_WM, "Figuring out where to add app window "
+                + client.asBinder() + " (token=" + this + ")");
+
+        final WindowToken wToken = win.mToken;
+
+        // Figure out where the window should go, based on the order of applications.
+        mTmpGetWindowOnDisplaySearchResult.reset();
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.getWindowOnDisplayBeforeToken(this, wToken, mTmpGetWindowOnDisplaySearchResult);
+            if (mTmpGetWindowOnDisplaySearchResult.reachedToken) {
+                // We have reach the token we are interested in. End search.
+                break;
+            }
+        }
+
+        WindowState pos = mTmpGetWindowOnDisplaySearchResult.foundWindow;
+
+        // We now know the index into the apps. If we found an app window above, that gives us the
+        // position; else we need to look some more.
+        if (pos != null) {
+            // Move behind any windows attached to this one.
+            final WindowToken atoken = mService.mTokenMap.get(pos.mClient.asBinder());
+            if (atoken != null) {
+                tokenWindowList = getTokenWindowsOnDisplay(atoken);
+                final int NC = tokenWindowList.size();
+                if (NC > 0) {
+                    WindowState bottom = tokenWindowList.get(0);
+                    if (bottom.mSubLayer < 0) {
+                        pos = bottom;
+                    }
+                }
+            }
+            addWindowToListBefore(win, pos);
+            return 0;
+        }
+
+        // Continue looking down until we find the first token that has windows on this display.
+        mTmpGetWindowOnDisplaySearchResult.reset();
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.getWindowOnDisplayAfterToken(this, wToken, mTmpGetWindowOnDisplaySearchResult);
+            if (mTmpGetWindowOnDisplaySearchResult.foundWindow != null) {
+                // We have found a window after the token. End search.
+                break;
+            }
+        }
+
+        pos = mTmpGetWindowOnDisplaySearchResult.foundWindow;
+
+        if (pos != null) {
+            // Move in front of any windows attached to this one.
+            final WindowToken atoken = mService.mTokenMap.get(pos.mClient.asBinder());
+            if (atoken != null) {
+                final WindowState top = atoken.getTopWindow();
+                if (top != null && top.mSubLayer >= 0) {
+                    pos = top;
+                }
+            }
+            addWindowToListAfter(win, pos);
+            return 0;
+        }
+
+        // Just search for the start of this layer.
+        final int myLayer = win.mBaseLayer;
+        int i;
+        for (i = mWindows.size() - 1; i >= 0; --i) {
+            final WindowState w = mWindows.get(i);
+            // Dock divider shares the base layer with application windows, but we want to always
+            // keep it above the application windows. The sharing of the base layer is intended
+            // for window animations, which need to be above the dock divider for the duration
+            // of the animation.
+            if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) {
+                break;
+            }
+        }
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                "Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+                + mWindows.size());
+        mWindows.add(i + 1, win);
+        mService.mWindowsChanged = true;
+        return 0;
+    }
+
+    /** Adds this non-app window to the window list. */
+    void addNonAppWindowToWindowList(WindowState win) {
+        // Figure out where window should go, based on layer.
+        int i;
+        for (i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState otherWin = mWindows.get(i);
+            if (otherWin.getBaseType() != TYPE_WALLPAPER && otherWin.mBaseLayer <= win.mBaseLayer) {
+                // Wallpaper wanders through the window list, for example to position itself
+                // directly behind keyguard. Because of this it will break the ordering based on
+                // WindowState.mBaseLayer. There might windows with higher mBaseLayer behind it and
+                // we don't want the new window to appear above them. An example of this is adding
+                // of the docked stack divider. Consider a scenario with the following ordering (top
+                // to bottom): keyguard, wallpaper, assist preview, apps. We want the dock divider
+                // to land below the assist preview, so the dock divider must ignore the wallpaper,
+                // with which it shares the base layer.
+                break;
+            }
+        }
+
+        i++;
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                "Free window: Adding window " + this + " at " + i + " of " + mWindows.size());
+        mWindows.add(i, win);
+        mService.mWindowsChanged = true;
+    }
+
+    void addChildWindowToWindowList(WindowState win) {
+        final WindowState parentWindow = win.getParentWindow();
+
+        WindowList windowsOnSameDisplay = getTokenWindowsOnDisplay(win.mToken);
+
+        // Figure out this window's ordering relative to the parent window.
+        final int wCount = windowsOnSameDisplay.size();
+        final int sublayer = win.mSubLayer;
+        int largestSublayer = Integer.MIN_VALUE;
+        WindowState windowWithLargestSublayer = null;
+        int i;
+        for (i = 0; i < wCount; i++) {
+            WindowState w = windowsOnSameDisplay.get(i);
+            final int wSublayer = w.mSubLayer;
+            if (wSublayer >= largestSublayer) {
+                largestSublayer = wSublayer;
+                windowWithLargestSublayer = w;
+            }
+            if (sublayer < 0) {
+                // For negative sublayers, we go below all windows in the same sublayer.
+                if (wSublayer >= sublayer) {
+                    addWindowToListBefore(win, wSublayer >= 0 ? parentWindow : w);
+                    break;
+                }
+            } else {
+                // For positive sublayers, we go above all windows in the same sublayer.
+                if (wSublayer > sublayer) {
+                    addWindowToListBefore(win, w);
+                    break;
+                }
+            }
+        }
+        if (i >= wCount) {
+            if (sublayer < 0) {
+                addWindowToListBefore(win, parentWindow);
+            } else {
+                addWindowToListAfter(win,
+                        largestSublayer >= 0 ? windowWithLargestSublayer : parentWindow);
+            }
+        }
+    }
+
+    /**
+     * Z-orders the display window list so that:
+     * <ul>
+     * <li>Any windows that are currently below the wallpaper window stay below the wallpaper
+     *      window.
+     * <li>Exiting application windows are at the bottom, but above the wallpaper window.
+     * <li>All other application windows are above the exiting application windows and ordered based
+     *      on the ordering of their stacks and tasks on the display.
+     * <li>Non-application windows are at the very top.
+     * </ul>
+     * <p>
+     * NOTE: This isn't a complete picture of what the user see. Further manipulation of the window
+     *       surface layering is done in {@link WindowLayersController}.
+     */
+    void rebuildAppWindowList() {
+        int count = mWindows.size();
+        int i;
+        int lastBelow = -1;
+        int numRemoved = 0;
+
+        if (mRebuildTmp.length < count) {
+            mRebuildTmp = new WindowState[count + 10];
+        }
+
+        // First remove all existing app windows.
+        i = 0;
+        while (i < count) {
+            final WindowState w = mWindows.get(i);
+            if (w.mAppToken != null) {
+                final WindowState win = mWindows.remove(i);
+                win.mRebuilding = true;
+                mRebuildTmp[numRemoved] = win;
+                mService.mWindowsChanged = true;
+                if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Rebuild removing window: " + win);
+                count--;
+                numRemoved++;
+                continue;
+            } else if (lastBelow == i-1) {
+                if (w.mAttrs.type == TYPE_WALLPAPER) {
+                    lastBelow = i;
+                }
+            }
+            i++;
+        }
+
+        // Keep whatever windows were below the app windows still below, by skipping them.
+        lastBelow++;
+        i = lastBelow;
+
+        // First add all of the exiting app tokens...  these are no longer in the main app list,
+        // but still have windows shown. We put them in the back because now that the animation is
+        // over we no longer will care about them.
+        final int numStacks = mStacks.size();
+        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+            AppTokenList exitingAppTokens = mStacks.get(stackNdx).mExitingAppTokens;
+            int NT = exitingAppTokens.size();
+            for (int j = 0; j < NT; j++) {
+                i = exitingAppTokens.get(j).rebuildWindowList(this, i);
+            }
+        }
+
+        // And add in the still active app tokens in Z order.
+        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+            i = mStacks.get(stackNdx).rebuildWindowList(this, i);
+        }
+
+        i -= lastBelow;
+        if (i != numRemoved) {
+            layoutNeeded = true;
+            Slog.w(TAG_WM, "On display=" + mDisplayId + " Rebuild removed " + numRemoved
+                    + " windows but added " + i + " rebuildAppWindowListLocked() "
+                    + " callers=" + Debug.getCallers(10));
+            for (i = 0; i < numRemoved; i++) {
+                WindowState ws = mRebuildTmp[i];
+                if (ws.mRebuilding) {
+                    StringWriter sw = new StringWriter();
+                    PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+                    ws.dump(pw, "", true);
+                    pw.flush();
+                    Slog.w(TAG_WM, "This window was lost: " + ws);
+                    Slog.w(TAG_WM, sw.toString());
+                    ws.mWinAnimator.destroySurfaceLocked();
+                }
+            }
+            Slog.w(TAG_WM, "Current app token list:");
+            dumpChildrenNames();
+            Slog.w(TAG_WM, "Final window list:");
+            dumpWindows();
+        }
+        Arrays.fill(mRebuildTmp, null);
+    }
+
+    /** Return the list of Windows on this display associated with the input token. */
+    WindowList getTokenWindowsOnDisplay(WindowToken token) {
+        final WindowList windowList = new WindowList();
+        final int count = mWindows.size();
+        for (int i = 0; i < count; i++) {
+            final WindowState win = mWindows.get(i);
+            if (win.mToken == token) {
+                windowList.add(win);
+            }
+        }
+        return windowList;
+    }
+
+    private int addAppWindowExisting(WindowState win, WindowList tokenWindowList) {
+
+        int tokenWindowsPos;
+        // If this application has existing windows, we simply place the new window on top of
+        // them... but keep the starting window on top.
+        if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
+            // Base windows go behind everything else.
+            final WindowState lowestWindow = tokenWindowList.get(0);
+            addWindowToListBefore(win, lowestWindow);
+            tokenWindowsPos = win.mToken.getWindowIndex(lowestWindow);
+        } else {
+            final AppWindowToken atoken = win.mAppToken;
+            final int windowListPos = tokenWindowList.size();
+            final WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
+            if (atoken != null && lastWindow == atoken.startingWindow) {
+                addWindowToListBefore(win, lastWindow);
+                tokenWindowsPos = win.mToken.getWindowIndex(lastWindow);
+            } else {
+                int newIdx = findIdxBasedOnAppTokens(win);
+                // There is a window above this one associated with the same apptoken note that the
+                // window could be a floating window that was created later or a window at the top
+                // of the list of windows associated with this token.
+                if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                        "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+                                + mWindows.size());
+                mWindows.add(newIdx + 1, win);
+                if (newIdx < 0) {
+                    // No window from token found on win's display.
+                    tokenWindowsPos = 0;
+                } else {
+                    tokenWindowsPos = win.mToken.getWindowIndex(mWindows.get(newIdx)) + 1;
+                }
+                mService.mWindowsChanged = true;
+            }
+        }
+        return tokenWindowsPos;
+    }
+
+    /** Places the first input window after the second input window in the window list. */
+    private void addWindowToListAfter(WindowState first, WindowState second) {
+        final int i = mWindows.indexOf(second);
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                "Adding window " + this + " at " + (i + 1) + " of " + mWindows.size()
+                + " (after " + second + ")");
+        mWindows.add(i + 1, first);
+        mService.mWindowsChanged = true;
+    }
+
+    /** Places the first input window before the second input window in the window list. */
+    private void addWindowToListBefore(WindowState first, WindowState second) {
+        int i = mWindows.indexOf(second);
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                "Adding window " + this + " at " + i + " of " + mWindows.size()
+                + " (before " + second + ")");
+        if (i < 0) {
+            Slog.w(TAG_WM, "addWindowToListBefore: Unable to find " + second + " in " + mWindows);
+            i = 0;
+        }
+        mWindows.add(i, first);
+        mService.mWindowsChanged = true;
+    }
+
+    /**
+     * This method finds out the index of a window that has the same app token as win. used for z
+     * ordering the windows in mWindows
+     */
+    private int findIdxBasedOnAppTokens(WindowState win) {
+        for(int j = mWindows.size() - 1; j >= 0; j--) {
+            final WindowState wentry = mWindows.get(j);
+            if(wentry.mAppToken == win.mAppToken) {
+                return j;
+            }
+        }
+        return -1;
+    }
+
+    private void dumpChildrenNames() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+        dumpChildrenNames(pw, "  ");
+    }
+
+    private void dumpChildrenNames(PrintWriter pw, String prefix) {
+        final String childPrefix = prefix + prefix;
+        for (int j = mStacks.size() - 1; j >= 0; j--) {
+            final TaskStack stack = mStacks.get(j);
+            pw.println("#" + j + " " + getName());
+            stack.dumpChildrenNames(pw, childPrefix);
+        }
+    }
+
+    private void dumpWindows() {
+        final int numDisplays = mService.mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
+            Slog.v(TAG_WM, " Display #" + displayContent.getDisplayId());
+            final WindowList windows = displayContent.getWindowList();
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                Slog.v(TAG_WM, "  #" + winNdx + ": " + windows.get(winNdx));
+            }
+        }
+    }
+
+    static final class GetWindowOnDisplaySearchResult {
+        boolean reachedToken;
+        WindowState foundWindow;
+
+        void reset() {
+            reachedToken = false;
+            foundWindow = null;
+        }
+    }
+
+    static final class TaskForResizePointSearchResult {
+        boolean searchDone;
+        Task taskForResize;
+
+        void reset() {
+            searchDone = false;
+            taskForResize = null;
+        }
+    }
+
+    void enableSurfaceTrace(FileDescriptor fd) {
+        for (int i = mWindows.size()  - 1; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+            win.mWinAnimator.enableSurfaceTrace(fd);
+        }
+    }
+
+    void disableSurfaceTrace() {
+        for (int i = mWindows.size()  - 1; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+            win.mWinAnimator.disableSurfaceTrace();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 0f5b042..ff676e9 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -153,7 +153,7 @@
         // If the bounds are fullscreen, return the value of the fullscreen configuration
         if (bounds == null || (bounds.left == 0 && bounds.top == 0
                 && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) {
-            return mService.mCurConfiguration.smallestScreenWidthDp;
+            return mService.mGlobalConfiguration.smallestScreenWidthDp;
         }
         final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
         final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
@@ -190,7 +190,7 @@
     }
 
     private void initSnapAlgorithmForRotations() {
-        final Configuration baseConfig = mService.mCurConfiguration;
+        final Configuration baseConfig = mService.mGlobalConfiguration;
 
         // Initialize the snap algorithms for all 4 screen orientations.
         final Configuration config = new Configuration();
@@ -203,7 +203,7 @@
                     ? mDisplayContent.mBaseDisplayWidth
                     : mDisplayContent.mBaseDisplayHeight;
             mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect);
-            config.setToDefaults();
+            config.unset();
             config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
             config.screenWidthDp = (int)
                     (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode) /
@@ -465,7 +465,7 @@
     }
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
         final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
@@ -482,7 +482,7 @@
         if (!visibleAndValid) {
             mDimLayer.hide();
         }
-        SurfaceControl.closeTransaction();
+        mService.closeSurfaceTransaction();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index d0167bb..588bb76 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -432,14 +432,14 @@
         // Move the surface to the given touch
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                 TAG_WM, ">>> OPEN TRANSACTION notifyMoveLw");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
             if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  DRAG "
                     + mSurfaceControl + ": pos=(" +
                     (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                     TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLw");
         }
diff --git a/services/core/java/com/android/server/wm/RemoteEventTrace.java b/services/core/java/com/android/server/wm/RemoteEventTrace.java
new file mode 100644
index 0000000..9f65ba3
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RemoteEventTrace.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 com.android.server.wm;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
+
+import android.util.Slog;
+import android.os.Debug;
+
+// Counterpart to remote surface trace for events which are not tied to a particular surface.
+class RemoteEventTrace {
+    private static final String TAG = "RemoteEventTrace";
+
+    // We terminate all our messages with a recognizable marker, to avoid issues
+    // with partial reads (which ADB makes impossible to avoid).
+    static final byte[] sigil = {(byte)0xfc, (byte)0xfc, (byte)0xfc, (byte)0xfc};
+
+    private final WindowManagerService mService;
+    private final DataOutputStream mOut;
+
+    RemoteEventTrace(WindowManagerService service, FileDescriptor fd) {
+        mService = service;
+        mOut = new DataOutputStream(new FileOutputStream(fd, false));
+    }
+
+    void openSurfaceTransaction() {
+        try {
+            mOut.writeUTF("OpenTransaction");
+            writeSigil();
+        } catch (Exception e) {
+            logException(e);
+            mService.disableSurfaceTrace();
+        }
+    }
+
+    void closeSurfaceTransaction() {
+        try {
+            mOut.writeUTF("CloseTransaction");
+            writeSigil();
+        } catch (Exception e) {
+            logException(e);
+            mService.disableSurfaceTrace();
+        }
+    }
+
+    private void writeSigil() throws Exception {
+        mOut.write(RemoteEventTrace.sigil, 0, 4);
+    }
+
+    static void logException(Exception e) {
+        Slog.i(TAG, "Exception writing to SurfaceTrace (client vanished?): " + e.toString());
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
new file mode 100644
index 0000000..0508fdf
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -0,0 +1,166 @@
+/*
+ * 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 android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.Slog;
+import android.view.SurfaceControl;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
+
+// A surface control subclass which logs events to a FD in binary format.
+// This can be used in our CTS tests to enable a pattern similar to mocking
+// the surface control.
+//
+// See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side.
+class RemoteSurfaceTrace extends SurfaceControl {
+    static final String TAG = "RemoteSurfaceTrace";
+
+    final FileDescriptor mWriteFd;
+    final DataOutputStream mOut;
+
+    final WindowManagerService mService;
+    final WindowState mWindow;
+
+    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped, WindowState window) {
+        super(wrapped);
+
+        mWriteFd = fd;
+        mOut = new DataOutputStream(new FileOutputStream(fd, false));
+
+        mWindow = window;
+        mService = mWindow.mService;
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+        writeFloatEvent("Alpha", alpha);
+        super.setAlpha(alpha);
+    }
+
+    @Override
+    public void setLayer(int zorder) {
+        writeIntEvent("Layer", zorder);
+        super.setLayer(zorder);
+    }
+
+    @Override
+    public void setPosition(float x, float y) {
+        writeFloatEvent("Position", x, y);
+        super.setPosition(x, y);
+    }
+
+    @Override
+    public void setGeometryAppliesWithResize() {
+        writeEvent("GeometryAppliesWithResize");
+        super.setGeometryAppliesWithResize();
+    }
+
+    @Override
+    public void setSize(int w, int h) {
+        writeIntEvent("Size", w, h);
+        super.setSize(w, h);
+    }
+
+    @Override
+    public void setWindowCrop(Rect crop) {
+        writeRectEvent("Crop", crop);
+        super.setWindowCrop(crop);
+    }
+
+    @Override
+    public void setFinalCrop(Rect crop) {
+        writeRectEvent("FinalCrop", crop);
+        super.setFinalCrop(crop);
+    }
+
+    @Override
+    public void setLayerStack(int layerStack) {
+        writeIntEvent("LayerStack", layerStack);
+        super.setLayerStack(layerStack);
+    }
+
+    @Override
+    public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+        writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+        super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+    }
+
+    @Override
+    public void hide() {
+        writeEvent("Hide");
+        super.hide();
+    }
+
+    @Override
+    public void show() {
+        writeEvent("Show");
+        super.show();
+    }
+
+    private void writeEvent(String tag) {
+        try {
+            mOut.writeUTF(tag);
+            mOut.writeUTF(mWindow.getWindowTag().toString());
+            writeSigil();
+        } catch (Exception e) {
+            RemoteEventTrace.logException(e);
+            mService.disableSurfaceTrace();
+        }
+    }
+
+    private void writeIntEvent(String tag, int... values) {
+        try {
+            mOut.writeUTF(tag);
+            mOut.writeUTF(mWindow.getWindowTag().toString());
+            for (int value: values) {
+                mOut.writeInt(value);
+            }
+            writeSigil();
+        } catch (Exception e) {
+            RemoteEventTrace.logException(e);
+            mService.disableSurfaceTrace();
+        }
+    }
+
+    private void writeFloatEvent(String tag, float... values) {
+        try {
+            mOut.writeUTF(tag);
+            mOut.writeUTF(mWindow.getWindowTag().toString());
+            for (float value: values) {
+                mOut.writeFloat(value);
+            }
+            writeSigil();
+        } catch (Exception e) {
+            RemoteEventTrace.logException(e);
+            mService.disableSurfaceTrace();
+        }
+    }
+
+    private void writeRectEvent(String tag, Rect value) {
+        writeFloatEvent(tag, value.left, value.top, value.right, value.bottom);
+    }
+
+    private void writeSigil() throws Exception {
+        mOut.write(RemoteEventTrace.sigil, 0, 4);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index c118a21..83337ca 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -147,6 +147,8 @@
     private boolean mMoreStartFrame;
     long mHalfwayPoint;
 
+    private final WindowManagerService mService;
+
     public void printTo(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
                 pw.print(" mWidth="); pw.print(mWidth);
@@ -216,7 +218,8 @@
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
             SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
-            boolean isSecure) {
+            boolean isSecure, WindowManagerService service) {
+        mService = service;
         mContext = context;
         mDisplayContent = displayContent;
         displayContent.getLogicalDisplayRect(mOriginalDisplayRect);
@@ -253,7 +256,7 @@
         if (!inTransaction) {
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                     ">>> OPEN TRANSACTION ScreenRotationAnimation");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
         }
 
         try {
@@ -295,7 +298,7 @@
             setRotationInTransaction(originalRotation);
         } finally {
             if (!inTransaction) {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                         "<<< CLOSE TRANSACTION ScreenRotationAnimation");
             }
@@ -546,7 +549,7 @@
             if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
                     TAG_WM,
                     ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
 
             // Compute the transformation matrix that must be applied
             // the the black frame to make it stay in the initial position
@@ -566,7 +569,7 @@
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
                         TAG_WM,
                         "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -577,7 +580,7 @@
             if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
                     TAG_WM,
                     ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
             try {
                 // Compute the transformation matrix that must be applied
                 // the the black frame to make it stay in the initial position
@@ -606,7 +609,7 @@
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
                         TAG_WM,
                         "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -617,7 +620,7 @@
             if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
                     TAG_WM,
                     ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
 
             try {
                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
@@ -628,7 +631,7 @@
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
                         TAG_WM,
                         "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index e9ce0ea..edd3a3b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -346,7 +346,7 @@
             final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                     TAG_WM, ">>> OPEN TRANSACTION performDrag");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
             try {
                 surfaceControl.setPosition(touchX - thumbCenterX,
                         touchY - thumbCenterY);
@@ -354,7 +354,7 @@
                 surfaceControl.setLayerStack(display.getLayerStack());
                 surfaceControl.show();
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
                         TAG_WM, "<<< CLOSE TRANSACTION performDrag");
             }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 545a439..9e8c609 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -34,14 +34,13 @@
 import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.Surface;
-import android.view.animation.Animation;
 
 import android.view.SurfaceControl;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
 
-class Task implements DimLayer.DimLayerUser {
+class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerUser {
     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
     // Return value from {@link setBounds} indicating no change was made to the Task bounds.
     static final int BOUNDS_CHANGE_NONE = 0;
@@ -50,8 +49,8 @@
     // Return value from {@link setBounds} indicating the size of the Task bounds changed.
     static final int BOUNDS_CHANGE_SIZE = 1 << 1;
 
+    // TODO: Track parent marks like this in WindowContainer.
     TaskStack mStack;
-    final AppTokenList mAppTokens = new AppTokenList();
     final int mTaskId;
     final int mUserId;
     boolean mDeferRemoval = false;
@@ -69,10 +68,12 @@
     int mRotation;
 
     // Whether mBounds is fullscreen
-    private boolean mFullscreen = true;
+    private boolean mFillsParent = true;
 
-    // Contains configurations settings that are different from the global configuration due to
-    // stack specific operations. E.g. {@link #setBounds}.
+    /**
+     * Contains configurations settings that are different from the parent configuration due to
+     * stack specific operations. E.g. {@link #setBounds}.
+     */
     Configuration mOverrideConfig = Configuration.EMPTY;
 
     // For comparison with DisplayContent bounds.
@@ -93,13 +94,13 @@
     private boolean mIsOnTopLauncher;
 
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
-            Configuration config, boolean isOnTopLauncher) {
+            Configuration overrideConfig, boolean isOnTopLauncher) {
         mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
         mService = service;
         mIsOnTopLauncher = isOnTopLauncher;
-        setBounds(bounds, config);
+        setBounds(bounds, overrideConfig);
     }
 
     DisplayContent getDisplayContent() {
@@ -107,18 +108,22 @@
     }
 
     void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) {
-        final int lastPos = mAppTokens.size();
+        final int lastPos = mChildren.size();
         if (addPos >= lastPos) {
             addPos = lastPos;
         } else {
             for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
-                if (mAppTokens.get(pos).removed) {
+                if (mChildren.get(pos).removed) {
                     // addPos assumes removed tokens are actually gone.
                     ++addPos;
                 }
             }
         }
-        mAppTokens.add(addPos, wtoken);
+
+        if (wtoken.mParent != null) {
+            wtoken.mParent.removeChild(wtoken);
+        }
+        addChild(wtoken, addPos);
         wtoken.mTask = this;
         mDeferRemoval = false;
         mResizeMode = resizeMode;
@@ -126,15 +131,16 @@
     }
 
     private boolean hasWindowsAlive() {
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            if (mAppTokens.get(i).hasWindowsAlive()) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            if (mChildren.get(i).hasWindowsAlive()) {
                 return true;
             }
         }
         return false;
     }
 
-    void removeLocked() {
+    @Override
+    void removeIfPossible() {
         if (hasWindowsAlive() && mStack.isAnimating()) {
             if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
             mDeferRemoval = true;
@@ -147,10 +153,11 @@
         if (content != null) {
             content.mDimLayerController.removeDimLayerUser(this);
         }
-        mStack.removeTask(this);
+        mParent.removeChild(this);
         mService.mTaskIdToTask.delete(mTaskId);
     }
 
+    // Change to use reparenting in WC when TaskStack is switched to use WC.
     void moveTaskToStack(TaskStack stack, boolean toTop) {
         if (stack == mStack) {
             return;
@@ -158,9 +165,7 @@
         if (DEBUG_STACK) Slog.i(TAG, "moveTaskToStack: removing taskId=" + mTaskId
                 + " from stack=" + mStack);
         EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
-        if (mStack != null) {
-            mStack.removeTask(this);
-        }
+        mParent.removeChild(this);
         stack.addTask(this, toTop);
     }
 
@@ -169,65 +174,60 @@
             if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
                     + " from stack=" + mStack);
             EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
-            mStack.removeTask(this);
+            mStack.removeChild(this);
         }
         stack.positionTask(this, position, showForAllUsers());
         resizeLocked(bounds, config, false /* force */);
 
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            mAppTokens.get(activityNdx).notifyMovedInStack();
+        for (int activityNdx = mChildren.size() - 1; activityNdx >= 0; --activityNdx) {
+            mChildren.get(activityNdx).notifyMovedInStack();
         }
     }
 
-    // TODO: Don't forget to switch to WC.detachChild
-    void detachChild(AppWindowToken wtoken) {
-        if (!removeAppToken(wtoken)) {
-            Slog.e(TAG, "detachChild: token=" + this + " not found.");
+    @Override
+    void removeChild(AppWindowToken token) {
+        if (!mChildren.contains(token)) {
+            Slog.e(TAG, "removeChild: token=" + this + " not found.");
+            return;
         }
-        mStack.mExitingAppTokens.remove(wtoken);
-    }
 
-    boolean removeAppToken(AppWindowToken wtoken) {
-        boolean removed = mAppTokens.remove(wtoken);
-        if (mAppTokens.size() == 0) {
+        super.removeChild(token);
+
+        if (mChildren.isEmpty()) {
             EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
             if (mDeferRemoval) {
-                removeLocked();
+                removeIfPossible();
             }
         }
-        wtoken.mTask = null;
-        /* Leave mTaskId for now, it might be useful for debug
-        wtoken.mTaskId = -1;
-         */
-        return removed;
+        token.mTask = null;
     }
 
     void setSendingToBottom(boolean toBottom) {
-        for (int appTokenNdx = 0; appTokenNdx < mAppTokens.size(); appTokenNdx++) {
-            mAppTokens.get(appTokenNdx).sendingToBottom = toBottom;
+        for (int appTokenNdx = 0; appTokenNdx < mChildren.size(); appTokenNdx++) {
+            mChildren.get(appTokenNdx).sendingToBottom = toBottom;
         }
     }
 
     /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
-    private int setBounds(Rect bounds, Configuration config) {
-        if (config == null) {
-            config = Configuration.EMPTY;
+    private int setBounds(Rect bounds, Configuration overrideConfig) {
+        if (overrideConfig == null) {
+            overrideConfig = Configuration.EMPTY;
         }
-        if (bounds == null && !Configuration.EMPTY.equals(config)) {
+        if (bounds == null && !Configuration.EMPTY.equals(overrideConfig)) {
             throw new IllegalArgumentException("null bounds but non empty configuration: "
-                    + config);
+                    + overrideConfig);
         }
-        if (bounds != null && Configuration.EMPTY.equals(config)) {
+        if (bounds != null && Configuration.EMPTY.equals(overrideConfig)) {
             throw new IllegalArgumentException("non null bounds, but empty configuration");
         }
-        boolean oldFullscreen = mFullscreen;
+        boolean oldFullscreen = mFillsParent;
         int rotation = Surface.ROTATION_0;
         final DisplayContent displayContent = mStack.getDisplayContent();
         if (displayContent != null) {
             displayContent.getLogicalDisplayRect(mTmpRect);
             rotation = displayContent.getDisplayInfo().rotation;
-            mFullscreen = bounds == null;
-            if (mFullscreen) {
+            mFillsParent = bounds == null;
+            if (mFillsParent) {
                 bounds = mTmpRect;
             }
         }
@@ -236,7 +236,7 @@
             // Can't set to fullscreen if we don't have a display to get bounds from...
             return BOUNDS_CHANGE_NONE;
         }
-        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+        if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) {
             return BOUNDS_CHANGE_NONE;
         }
 
@@ -254,7 +254,7 @@
         if (displayContent != null) {
             displayContent.mDimLayerController.updateDimLayer(this);
         }
-        mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
+        mOverrideConfig = mFillsParent ? Configuration.EMPTY : overrideConfig;
         return boundsChange;
     }
 
@@ -299,12 +299,8 @@
         return mHomeTask;
     }
 
-    private boolean inCropWindowsResizeMode() {
-        return !mHomeTask && !isResizeable() && mResizeMode == RESIZE_MODE_CROP_WINDOWS;
-    }
-
-    boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
-        int boundsChanged = setBounds(bounds, configuration);
+    boolean resizeLocked(Rect bounds, Configuration overrideConfig, boolean forced) {
+        int boundsChanged = setBounds(bounds, overrideConfig);
         if (forced) {
             boundsChanged |= BOUNDS_CHANGE_SIZE;
         }
@@ -325,7 +321,7 @@
      */
     void prepareFreezingBounds() {
         mPreparedFrozenBounds.set(mBounds);
-        mPreparedFrozenMergedConfig.setTo(mService.mCurConfiguration);
+        mPreparedFrozenMergedConfig.setTo(mService.mGlobalConfiguration);
         mPreparedFrozenMergedConfig.updateFrom(mOverrideConfig);
     }
 
@@ -358,13 +354,10 @@
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
     private boolean useCurrentBounds() {
         final DisplayContent displayContent = mStack.getDisplayContent();
-        if (mFullscreen
+        return mFillsParent
                 || !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
                 || displayContent == null
-                || displayContent.getDockedStackVisibleForUserLocked() != null) {
-            return true;
-        }
-        return false;
+                || displayContent.getDockedStackVisibleForUserLocked() != null;
     }
 
     /** Original bounds of the task if applicable, otherwise fullscreen rect. */
@@ -395,8 +388,8 @@
      */
     boolean getMaxVisibleBounds(Rect out) {
         boolean foundTop = false;
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            final AppWindowToken token = mAppTokens.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final AppWindowToken token = mChildren.get(i);
             // skip hidden (or about to hide) apps
             if (token.mIsExiting || token.clientHidden || token.hiddenRequested) {
                 continue;
@@ -432,14 +425,14 @@
         final DisplayContent displayContent = mStack.getDisplayContent();
         // It doesn't matter if we in particular are part of the resize, since we couldn't have
         // a DimLayer anyway if we weren't visible.
-        final boolean dockedResizing = displayContent != null ?
-                displayContent.mDividerControllerLocked.isResizing() : false;
+        final boolean dockedResizing = displayContent != null
+                && displayContent.mDividerControllerLocked.isResizing();
         if (useCurrentBounds()) {
             if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
                 return;
             }
 
-            if (!mFullscreen) {
+            if (!mFillsParent) {
                 // When minimizing the docked stack when going home, we don't adjust the task bounds
                 // so we need to intersect the task bounds with the stack bounds here.
                 //
@@ -459,10 +452,11 @@
             return;
         }
 
-        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
-        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
-        // system.
-        displayContent.getLogicalDisplayRect(out);
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is
+        // not currently visible. Go ahead a represent it as fullscreen to the rest of the system.
+        if (displayContent != null) {
+            displayContent.getLogicalDisplayRect(out);
+        }
     }
 
     void setDragResizing(boolean dragResizing, int dragResizeMode) {
@@ -477,12 +471,6 @@
         }
     }
 
-    private void resetDragResizingChangeReported() {
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            mAppTokens.get(activityNdx).resetDragResizingChangeReported();
-        }
-    }
-
     boolean isDragResizing() {
         return mDragResizing;
     }
@@ -491,29 +479,11 @@
         return mDragResizeMode;
     }
 
-    /**
-     * Adds all of the tasks windows to {@link WindowManagerService#mWaitingForDrawn} if drag
-     * resizing state of the window has been changed.
-     */
-    void setWaitingForDrawnIfResizingChanged() {
-        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
-            mAppTokens.get(i).setWaitingForDrawnIfResizingChanged();
-        }
-    }
-
-    boolean detachFromDisplay() {
-        boolean didSomething = false;
-        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
-            didSomething |= mAppTokens.get(i).detachFromDisplay();
-        }
-        return didSomething;
-    }
-
     void updateDisplayInfo(final DisplayContent displayContent) {
         if (displayContent == null) {
             return;
         }
-        if (mFullscreen) {
+        if (mFillsParent) {
             setBounds(null, Configuration.EMPTY);
             return;
         }
@@ -544,59 +514,23 @@
         }
     }
 
-    private void onResize() {
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            mAppTokens.get(activityNdx).onResize();
-        }
-    }
-
-    private void onMovedByResize() {
-        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
-            mAppTokens.get(i).onMovedByResize();
-        }
-    }
-
-    /**
-     * Cancels any running app transitions associated with the task.
-     */
+    /** Cancels any running app transitions associated with the task. */
     void cancelTaskWindowTransition() {
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            mAppTokens.get(activityNdx).mAppAnimator.clearAnimation();
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).mAppAnimator.clearAnimation();
         }
     }
 
-    /**
-     * Cancels any running thumbnail transitions associated with the task.
-     */
+    /** Cancels any running thumbnail transitions associated with the task. */
     void cancelTaskThumbnailTransition() {
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            mAppTokens.get(activityNdx).mAppAnimator.clearThumbnail();
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).mAppAnimator.clearThumbnail();
         }
     }
 
     boolean showForAllUsers() {
-        final int tokensCount = mAppTokens.size();
-        return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
-    }
-
-    boolean hasContentToDisplay() {
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            final AppWindowToken appToken = mAppTokens.get(i);
-            if (appToken.hasContentToDisplay()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean isVisible() {
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            final AppWindowToken appToken = mAppTokens.get(i);
-            if (appToken.isVisible()) {
-                return true;
-            }
-        }
-        return false;
+        final int tokensCount = mChildren.size();
+        return (tokensCount != 0) && mChildren.get(tokensCount - 1).showForAllUsers;
     }
 
     boolean inHomeStack() {
@@ -621,8 +555,8 @@
     }
 
     AppWindowToken getTopVisibleAppToken() {
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            final AppWindowToken token = mAppTokens.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final AppWindowToken token = mChildren.get(i);
             // skip hidden (or about to hide) apps
             if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) {
                 return token;
@@ -638,7 +572,7 @@
 
     boolean isFullscreen() {
         if (useCurrentBounds()) {
-            return mFullscreen;
+            return mFillsParent;
         }
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
@@ -651,39 +585,69 @@
         return mStack.getDisplayContent().getDisplayInfo();
     }
 
-    /**
-     * See {@link WindowManagerService#overridePlayingAppAnimationsLw}
-     */
-    void overridePlayingAppAnimations(Animation a) {
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            mAppTokens.get(i).overridePlayingAppAnimations(a);
-        }
-    }
-
     void forceWindowsScaleable(boolean force) {
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
-            for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-                mAppTokens.get(i).forceWindowsScaleableInTransaction(force);
+            for (int i = mChildren.size() - 1; i >= 0; i--) {
+                mChildren.get(i).forceWindowsScaleableInTransaction(force);
             }
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
         }
     }
 
-    boolean isAnimating() {
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            final AppWindowToken aToken = mAppTokens.get(i);
-            if (aToken.isAnimating()) {
-                return true;
+    void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token,
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final AppWindowToken current = mChildren.get(i);
+            if (current == token) {
+                // We have reach the token we are interested in. End search.
+                result.reachedToken = true;
+                return;
+            }
+
+            // We haven't reached the token yet; if this token is not going to the bottom and
+            // has windows on this display, then it is a candidate for what we are looking for.
+            final WindowList tokenWindowList = dc.getTokenWindowsOnDisplay(current);
+            if (!current.sendingToBottom && tokenWindowList.size() > 0) {
+                result.foundWindow = tokenWindowList.get(0);
             }
         }
-        return false;
+    }
+
+    void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token,
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final AppWindowToken current = mChildren.get(i);
+            if (!result.reachedToken) {
+                if (current == token) {
+                    // We have reached the token we are interested in. Get whichever window occurs
+                    // after it that is on the same display.
+                    result.reachedToken = true;
+                }
+                continue;
+            }
+
+            final WindowList tokenWindowList = dc.getTokenWindowsOnDisplay(current);
+            if (tokenWindowList.size() > 0) {
+                result.foundWindow = tokenWindowList.get(tokenWindowList.size() - 1);
+                return;
+            }
+        }
+    }
+
+    @Override
+    boolean fillsParent() {
+        return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId);
     }
 
     @Override
     public String toString() {
-        return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
+        return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
+    }
+
+    String getName() {
+        return toShortString();
     }
 
     @Override
@@ -695,16 +659,16 @@
         final String doublePrefix = prefix + "  ";
 
         pw.println(prefix + "taskId=" + mTaskId);
-        pw.println(doublePrefix + "mFullscreen=" + mFullscreen);
+        pw.println(doublePrefix + "mFillsParent=" + mFillsParent);
         pw.println(doublePrefix + "mBounds=" + mBounds.toShortString());
         pw.println(doublePrefix + "mdr=" + mDeferRemoval);
-        pw.println(doublePrefix + "appTokens=" + mAppTokens);
+        pw.println(doublePrefix + "appTokens=" + mChildren);
         pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
 
         final String triplePrefix = doublePrefix + "  ";
 
-        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
-            final AppWindowToken wtoken = mAppTokens.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final AppWindowToken wtoken = mChildren.get(i);
             pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
             wtoken.dump(pw, triplePrefix);
         }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 5c321a1..21db840 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -459,13 +459,13 @@
         mCurrentDimSide = dimSide;
 
         if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         if (mCurrentDimSide == CTRL_NONE) {
             mDimLayer.hide();
         } else {
             showDimLayer();
         }
-        SurfaceControl.closeTransaction();
+        mService.closeSurfaceTransaction();
     }
 
     /**
@@ -477,8 +477,8 @@
      */
     private int getDimSide(int x) {
         if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
-                || !mTask.mStack.isFullscreen()
-                || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
+                || !mTask.mStack.fillsParent()
+                || mService.mGlobalConfiguration.orientation != ORIENTATION_LANDSCAPE) {
             return CTRL_NONE;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index cf31310..b24a06a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.DOCKED_BOTTOM;
@@ -29,6 +30,7 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
@@ -36,6 +38,7 @@
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Debug;
 import android.os.RemoteException;
 import android.util.EventLog;
@@ -43,17 +46,16 @@
 import android.util.SparseArray;
 import android.view.DisplayInfo;
 import android.view.Surface;
-import android.view.animation.Animation;
 
+import android.view.WindowManagerPolicy;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
-public class TaskStack implements DimLayer.DimLayerUser,
+public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
     /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
      * restrict IME adjustment so that a min portion of top stack remains visible.*/
@@ -69,12 +71,9 @@
     private final WindowManagerService mService;
 
     /** The display this stack sits under. */
+    // TODO: Track parent marks like this in WindowContainer.
     private DisplayContent mDisplayContent;
 
-    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
-     * mTaskHistory in the ActivityStack with the same mStackId */
-    private final ArrayList<Task> mTasks = new ArrayList<>();
-
     /** For comparison with DisplayContent bounds. */
     private Rect mTmpRect = new Rect();
     private Rect mTmpRect2 = new Rect();
@@ -92,7 +91,7 @@
     private final Rect mFullyAdjustedImeBounds = new Rect();
 
     /** Whether mBounds is fullscreen */
-    private boolean mFullscreen = true;
+    private boolean mFillsParent = true;
 
     // Device rotation as of the last time {@link #mBounds} was set.
     int mRotation;
@@ -110,6 +109,7 @@
     final AppTokenList mExitingAppTokens = new AppTokenList();
 
     /** Detach this stack from its display when animation completes. */
+    // TODO: maybe tie this to WindowContainer#removeChild some how...
     boolean mDeferDetach;
 
     private final Rect mTmpAdjustedBounds = new Rect();
@@ -121,11 +121,9 @@
     private float mAdjustDividerAmount;
     private final int mDockedStackMinimizeThickness;
 
-    // If this is true, we are in the bounds animating mode.
-    // The task will be down or upscaled to perfectly fit the
-    // region it would have been cropped to. We may also avoid
-    // certain logic we would otherwise apply while resizing,
-    // while resizing in the bounds animating mode.
+    // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
+    // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
+    // would otherwise apply while resizing, while resizing in the bounds animating mode.
     private boolean mBoundsAnimating = false;
 
     // Temporary storage for the new bounds that should be used after the configuration change.
@@ -144,29 +142,25 @@
         return mDisplayContent;
     }
 
-    ArrayList<Task> getTasks() {
-        return mTasks;
-    }
-
     Task findHomeTask() {
         if (mStackId != HOME_STACK_ID) {
             return null;
         }
 
-        for (int i = mTasks.size() - 1; i >= 0; i--) {
-            if (mTasks.get(i).isHomeTask()) {
-                return mTasks.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            if (mChildren.get(i).isHomeTask()) {
+                return mChildren.get(i);
             }
         }
         return null;
     }
 
     boolean hasMultipleTaskWithHomeTaskNotTop() {
-        return mTasks.size() > 1 && !mTasks.get(mTasks.size() - 1).isHomeTask();
+        return mChildren.size() > 1 && !mChildren.get(mChildren.size() - 1).isHomeTask();
     }
 
     boolean topTaskIsOnTopLauncher() {
-        return mTasks.get(mTasks.size() - 1).isOnTopLauncher();
+        return mChildren.get(mChildren.size() - 1).isOnTopLauncher();
     }
 
     /**
@@ -182,8 +176,8 @@
         setBounds(stackBounds);
 
         // Update bounds of containing tasks.
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = mTasks.get(taskNdx);
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mChildren.get(taskNdx);
             Configuration config = configs.get(task.mTaskId);
             if (config != null) {
                 Rect bounds = taskBounds.get(task.mTaskId);
@@ -198,8 +192,8 @@
     }
 
     void prepareFreezingTaskBounds() {
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = mTasks.get(taskNdx);
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mChildren.get(taskNdx);
             task.prepareFreezingBounds();
         }
     }
@@ -240,29 +234,29 @@
     }
 
     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
-        if (mFullscreen) {
+        if (mFillsParent) {
             return;
         }
 
         final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
 
         // Update bounds of containing tasks.
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = mTasks.get(taskNdx);
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mChildren.get(taskNdx);
             task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
         }
     }
 
     private boolean setBounds(Rect bounds) {
-        boolean oldFullscreen = mFullscreen;
+        boolean oldFullscreen = mFillsParent;
         int rotation = Surface.ROTATION_0;
         int density = DENSITY_DPI_UNDEFINED;
         if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
             rotation = mDisplayContent.getDisplayInfo().rotation;
             density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
-            mFullscreen = bounds == null;
-            if (mFullscreen) {
+            mFillsParent = bounds == null;
+            if (mFillsParent) {
                 bounds = mTmpRect;
             }
         }
@@ -271,7 +265,7 @@
             // Can't set to fullscreen if we don't have a display to get bounds from...
             return false;
         }
-        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+        if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) {
             return false;
         }
 
@@ -299,7 +293,7 @@
 
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
     private boolean useCurrentBounds() {
-        if (mFullscreen
+        if (mFillsParent
                 || !StackId.isResizeableByDockedStack(mStackId)
                 || mDisplayContent == null
                 || mDisplayContent.getDockedStackLocked() != null) {
@@ -339,13 +333,13 @@
             return;
         }
 
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+            mChildren.get(taskNdx).updateDisplayInfo(mDisplayContent);
         }
         if (bounds != null) {
             setBounds(bounds);
             return;
-        } else if (mFullscreen) {
+        } else if (mFillsParent) {
             setBounds(null);
             return;
         }
@@ -378,7 +372,7 @@
             return false;
         }
 
-        if (mFullscreen) {
+        if (mFillsParent) {
             // Update stack bounds again since rotation changed since updateDisplayInfo().
             setBounds(null);
             // Return false since we don't need the client to resize.
@@ -465,7 +459,7 @@
 
         // Snap the position to a target.
         final int rotation = displayInfo.rotation;
-        final int orientation = mService.mCurConfiguration.orientation;
+        final int orientation = mService.mGlobalConfiguration.orientation;
         mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
         final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
                 mService.mContext.getResources(), displayWidth, displayHeight,
@@ -478,16 +472,7 @@
                 dividerSize);
     }
 
-    boolean isAnimating() {
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = mTasks.get(taskNdx);
-            if (task.isAnimating()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+    // TODO: Checkout the call points of this method and the ones below to see how they can fit in WC.
     void addTask(Task task, boolean toTop) {
         addTask(task, toTop, task.showForAllUsers());
     }
@@ -499,14 +484,17 @@
      * @param showForAllUsers Whether to show the task regardless of the current user.
      */
     void addTask(Task task, boolean toTop, boolean showForAllUsers) {
-        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
+        positionTask(task, toTop ? mChildren.size() : 0, showForAllUsers);
     }
 
+    // TODO: We should really have users as a window container in the hierarchy so that we don't
+    // have to do complicated things like we are doing in this method and also also the method to
+    // just be WindowContainer#addChild
     void positionTask(Task task, int position, boolean showForAllUsers) {
         final boolean canShowTask =
                 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
-        mTasks.remove(task);
-        int stackSize = mTasks.size();
+        removeChild(task);
+        int stackSize = mChildren.size();
         int minPosition = 0;
         int maxPosition = stackSize;
 
@@ -520,11 +508,13 @@
 
         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM,
                 "positionTask: task=" + task + " position=" + position);
-        mTasks.add(position, task);
+        addChild(task, position);
         task.mStack = this;
         task.updateDisplayInfo(mDisplayContent);
-        boolean toTop = position == mTasks.size() - 1;
+        boolean toTop = position == mChildren.size() - 1;
         if (toTop) {
+            // TODO: Have a WidnowContainer method that moves all parents of a container to the
+            // front for cases like this.
             mDisplayContent.moveStack(this, true);
         }
 
@@ -546,7 +536,7 @@
      */
     private int computeMinPosition(int minPosition, int size) {
         while (minPosition < size) {
-            final Task tmpTask = mTasks.get(minPosition);
+            final Task tmpTask = mChildren.get(minPosition);
             final boolean canShowTmpTask =
                     tmpTask.showForAllUsers()
                             || mService.isCurrentProfileLocked(tmpTask.mUserId);
@@ -565,7 +555,7 @@
      */
     private int computeMaxPosition(int maxPosition) {
         while (maxPosition > 0) {
-            final Task tmpTask = mTasks.get(maxPosition - 1);
+            final Task tmpTask = mChildren.get(maxPosition - 1);
             final boolean canShowTmpTask =
                     tmpTask.showForAllUsers()
                             || mService.isCurrentProfileLocked(tmpTask.mUserId);
@@ -577,16 +567,17 @@
         return maxPosition;
     }
 
+    // TODO: Have functionality in WC to move things to the bottom or top. Also, look at the call
+    // points for this methods to see if we need functionality to move something to the front/bottom
+    // with its parents.
     void moveTaskToTop(Task task) {
         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers="
                 + Debug.getCallers(6));
-        mTasks.remove(task);
         addTask(task, true);
     }
 
     void moveTaskToBottom(Task task) {
         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task);
-        mTasks.remove(task);
         addTask(task, false);
     }
 
@@ -595,11 +586,18 @@
      * back.
      * @param task The Task to delete.
      */
-    void removeTask(Task task) {
-        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeTask: task=" + task);
-        mTasks.remove(task);
+    @Override
+    void removeChild(Task task) {
+        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task);
+        if (!mChildren.contains(task)) {
+            Slog.e(TAG_WM, "removeChild: task=" + this + " not found.");
+            return;
+        }
+
+        super.removeChild(task);
+
         if (mDisplayContent != null) {
-            if (mTasks.isEmpty()) {
+            if (mChildren.isEmpty()) {
                 mDisplayContent.moveStack(this, false);
             }
             mDisplayContent.layoutNeeded = true;
@@ -626,7 +624,7 @@
         final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
         if (mStackId == DOCKED_STACK_ID
                 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId)
-                        && !dockedStack.isFullscreen())) {
+                        && !dockedStack.fillsParent())) {
             // The existence of a docked stack affects the size of other static stack created since
             // the docked stack occupies a dedicated region on screen, but only if the dock stack is
             // not fullscreen. If it's fullscreen, it means that we are in the transition of
@@ -717,7 +715,7 @@
                     di.logicalWidth,
                     di.logicalHeight,
                     dockDividerWidth,
-                    mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT,
+                    mService.mGlobalConfiguration.orientation == ORIENTATION_PORTRAIT,
                     mTmpRect2).getMiddleTarget().position;
 
             if (dockOnTopOrLeft) {
@@ -766,14 +764,10 @@
                 1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
     }
 
+    // TODO: Should this really be removeImmidiately or removeChild?
     boolean detachFromDisplay() {
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
-
-        boolean didSomething = false;
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            didSomething |= mTasks.get(taskNdx).detachFromDisplay();
-        }
-
+        boolean didSomething = super.detachFromDisplay();
         close();
         return didSomething;
     }
@@ -794,13 +788,14 @@
         }
     }
 
+    // TODO: Should each user have there own stacks?
     void switchUser() {
-        int top = mTasks.size();
+        int top = mChildren.size();
         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
-            Task task = mTasks.get(taskNdx);
+            Task task = mChildren.get(taskNdx);
             if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
-                mTasks.remove(taskNdx);
-                mTasks.add(task);
+                mChildren.remove(taskNdx);
+                mChildren.add(task);
                 --top;
             }
         }
@@ -903,8 +898,8 @@
      * to the list of to be drawn windows the service is waiting for.
      */
     void beginImeAdjustAnimation() {
-        for (int j = mTasks.size() - 1; j >= 0; j--) {
-            final Task task = mTasks.get(j);
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            final Task task = mChildren.get(j);
             if (task.hasContentToDisplay()) {
                 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
                 task.setWaitingForDrawnIfResizingChanged();
@@ -916,8 +911,8 @@
      * Resets the resizing state of all windows.
      */
     void endImeAdjustAnimation() {
-        for (int j = mTasks.size() - 1; j >= 0; j--) {
-            mTasks.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
         }
     }
 
@@ -1083,10 +1078,10 @@
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "mStackId=" + mStackId);
         pw.println(prefix + "mDeferDetach=" + mDeferDetach);
-        pw.println(prefix + "mFullscreen=" + mFullscreen);
+        pw.println(prefix + "mFillsParent=" + mFillsParent);
         pw.println(prefix + "mBounds=" + mBounds.toShortString());
         if (mMinimizeAmount != 0f) {
-            pw.println(prefix + "mMinimizeAmout=" + mMinimizeAmount);
+            pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
         }
         if (mAdjustedForIme) {
             pw.println(prefix + "mAdjustedForIme=true");
@@ -1096,8 +1091,8 @@
         if (!mAdjustedBounds.isEmpty()) {
             pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
         }
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
-            mTasks.get(taskNdx).dump(prefix + "  ", pw);
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
+            mChildren.get(taskNdx).dump(prefix + "  ", pw);
         }
         if (mAnimationBackgroundSurface.isDimming()) {
             pw.println(prefix + "mWindowAnimationBackgroundSurface:");
@@ -1118,20 +1113,21 @@
 
     /** Fullscreen status of the stack without adjusting for other factors in the system like
      * visibility of docked stack.
-     * Most callers should be using {@link #isFullscreen} as it take into consideration other
+     * Most callers should be using {@link #fillsParent} as it take into consideration other
      * system factors. */
     boolean getRawFullscreen() {
-        return mFullscreen;
+        return mFillsParent;
     }
 
     @Override
     public boolean dimFullscreen() {
-        return mStackId == HOME_STACK_ID || isFullscreen();
+        return mStackId == HOME_STACK_ID || fillsParent();
     }
 
-    boolean isFullscreen() {
+    @Override
+    boolean fillsParent() {
         if (useCurrentBounds()) {
-            return mFullscreen;
+            return mFillsParent;
         }
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
@@ -1146,7 +1142,11 @@
 
     @Override
     public String toString() {
-        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
+        return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
+    }
+
+    String getName() {
+        return toShortString();
     }
 
     @Override
@@ -1170,7 +1170,7 @@
             return DOCKED_INVALID;
         }
         mDisplayContent.getLogicalDisplayRect(mTmpRect);
-        final int orientation = mService.mCurConfiguration.orientation;
+        final int orientation = mService.mGlobalConfiguration.orientation;
         return getDockSideUnchecked(bounds, mTmpRect, orientation);
     }
 
@@ -1206,16 +1206,125 @@
             return false;
         }
 
-        for (int i = mTasks.size() - 1; i >= 0; i--) {
-            final Task task = mTasks.get(i);
-            if (task.isVisible()) {
+        return super.isVisible();
+    }
+
+    boolean hasTaskForUser(int userId) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final Task task = mChildren.get(i);
+            if (task.mUserId == userId) {
                 return true;
             }
         }
-
         return false;
     }
 
+    int taskIdFromPoint(int x, int y) {
+        getBounds(mTmpRect);
+        if (!mTmpRect.contains(x, y) || isAdjustedForMinimizedDockedStack()) {
+            return -1;
+        }
+
+        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mChildren.get(taskNdx);
+            final WindowState win = task.getTopVisibleAppMainWindow();
+            if (win == null) {
+                continue;
+            }
+            // We need to use the task's dim bounds (which is derived from the visible bounds of its
+            // apps windows) for any touch-related tests. Can't use the task's original bounds
+            // because it might be adjusted to fit the content frame. For example, the presence of
+            // the IME adjusting the windows frames when the app window is the IME target.
+            task.getDimBounds(mTmpRect);
+            if (mTmpRect.contains(x, y)) {
+                return task.mTaskId;
+            }
+        }
+
+        return -1;
+    }
+
+    void findTaskForResizePoint(int x, int y, int delta,
+            DisplayContent.TaskForResizePointSearchResult results) {
+        if (!StackId.isTaskResizeAllowed(mStackId)) {
+            results.searchDone = true;
+            return;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final Task task = mChildren.get(i);
+            if (task.isFullscreen()) {
+                results.searchDone = true;
+                return;
+            }
+
+            // We need to use the task's dim bounds (which is derived from the visible bounds of
+            // its apps windows) for any touch-related tests. Can't use the task's original
+            // bounds because it might be adjusted to fit the content frame. One example is when
+            // the task is put to top-left quadrant, the actual visible area would not start at
+            // (0,0) after it's adjusted for the status bar.
+            task.getDimBounds(mTmpRect);
+            mTmpRect.inset(-delta, -delta);
+            if (mTmpRect.contains(x, y)) {
+                mTmpRect.inset(delta, delta);
+
+                results.searchDone = true;
+
+                if (!mTmpRect.contains(x, y)) {
+                    results.taskForResize = task;
+                    return;
+                }
+                // User touched inside the task. No need to look further,
+                // focus transfer will be handled in ACTION_UP.
+                return;
+            }
+        }
+    }
+
+    void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
+            Rect contentRect, Rect postExclude) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final Task task = mChildren.get(i);
+            AppWindowToken token = task.getTopVisibleAppToken();
+            if (token == null || !token.hasContentToDisplay()) {
+                continue;
+            }
+
+            /**
+             * Exclusion region is the region that TapDetector doesn't care about.
+             * Here we want to remove all non-focused tasks from the exclusion region.
+             * We also remove the outside touch area for resizing for all freeform
+             * tasks (including the focused).
+             *
+             * We save the focused task region once we find it, and add it back at the end.
+             */
+
+            task.getDimBounds(mTmpRect);
+
+            if (task == focusedTask) {
+                // Add the focused task rect back into the exclude region once we are done
+                // processing stacks.
+                postExclude.set(mTmpRect);
+            }
+
+            final boolean isFreeformed = task.inFreeformWorkspace();
+            if (task != focusedTask || isFreeformed) {
+                if (isFreeformed) {
+                    // If the task is freeformed, enlarge the area to account for outside
+                    // touch area for resize.
+                    mTmpRect.inset(-delta, -delta);
+                    // Intersect with display content rect. If we have system decor (status bar/
+                    // navigation bar), we want to exclude that from the tap detection.
+                    // Otherwise, if the app is partially placed under some system button (eg.
+                    // Recents, Home), pressing that button would cause a full series of
+                    // unwanted transfer focus/resume/pause, before we could go home.
+                    mTmpRect.intersect(contentRect);
+                }
+                touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+            }
+        }
+    }
+
     @Override  // AnimatesBounds
     public boolean setSize(Rect bounds) {
         synchronized (mService.mWindowMap) {
@@ -1297,12 +1406,68 @@
         return mBoundsAnimating;
     }
 
-    /**
-     * See {@link WindowManagerService#overridePlayingAppAnimationsLw}
-     */
-    void overridePlayingAppAnimations(Animation a) {
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            mTasks.get(i).overridePlayingAppAnimations(a);
+    /** Returns true if a removal action is still being deferred. */
+    boolean checkCompleteDeferredRemoval() {
+        if (isAnimating()) {
+            return true;
         }
+        if (mDeferDetach) {
+            mDisplayContent.detachChild(this);
+        }
+
+        return super.checkCompleteDeferredRemoval();
+    }
+
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        super.stepAppWindowsAnimation(currentTime, displayId);
+
+        // TODO: Why aren't we just using the loop above for this? mAppAnimator.animating isn't set
+        // below but is set in the loop above. See if it really matters...
+        final int exitingCount = mExitingAppTokens.size();
+        for (int i = 0; i < exitingCount; i++) {
+            final AppWindowAnimator appAnimator = mExitingAppTokens.get(i).mAppAnimator;
+            appAnimator.wasAnimating = appAnimator.animating;
+            if (appAnimator.stepAnimationLocked(currentTime, displayId)) {
+                mService.mAnimator.setAnimating(true);
+                mService.mAnimator.mAppWindowAnimating = true;
+            } else if (appAnimator.wasAnimating) {
+                // stopped animating, do one more pass through the layout
+                appAnimator.mAppToken.setAppLayoutChanges(
+                        WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+                        "exiting appToken " + appAnimator.mAppToken + " done", displayId);
+                if (DEBUG_ANIM) Slog.v(TAG_WM,
+                        "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
+            }
+        }
+    }
+
+    void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token,
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final Task task = mChildren.get(i);
+            task.getWindowOnDisplayBeforeToken(dc, token, result);
+            if (result.reachedToken) {
+                // We have reach the token we are interested in. End search.
+                return;
+            }
+        }
+    }
+
+    void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token,
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final Task task = mChildren.get(i);
+            task.getWindowOnDisplayAfterToken(dc, token, result);
+            if (result.foundWindow != null) {
+                // We have found a window after the token. End search.
+                return;
+            }
+        }
+    }
+
+    @Override
+    int getOrientation() {
+        return (StackId.canSpecifyOrientation(mStackId))
+                ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 5d2a52a..a13b6a1 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -29,7 +29,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -121,6 +120,9 @@
 
     private final AppTokenList mTmpExitingAppTokens = new AppTokenList();
 
+    /** The window that was previously hiding the Keyguard. */
+    private WindowState mLastShowWinWhenLocked;
+
     private String forceHidingToString() {
         switch (mForceHiding) {
             case KEYGUARD_NOT_SHOWN:    return "KEYGUARD_NOT_SHOWN";
@@ -166,59 +168,21 @@
         mDisplayContentsAnimators.delete(displayId);
     }
 
-    private void updateAppWindowsLocked(int displayId) {
-        ArrayList<TaskStack> stacks = mService.getDisplayContentLocked(displayId).getStacks();
-        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = stacks.get(stackNdx);
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                    final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator;
-                    appAnimator.wasAnimating = appAnimator.animating;
-                    if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
-                        appAnimator.animating = true;
-                        setAnimating(true);
-                        mAppWindowAnimating = true;
-                    } else if (appAnimator.wasAnimating) {
-                        // stopped animating, do one more pass through the layout
-                        setAppLayoutChanges(appAnimator,
-                                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                                "appToken " + appAnimator.mAppToken + " done", displayId);
-                        if (DEBUG_ANIM) Slog.v(TAG,
-                                "updateWindowsApps...: done animating " + appAnimator.mAppToken);
-                    }
-                }
-            }
-
-            mTmpExitingAppTokens.clear();
-            mTmpExitingAppTokens.addAll(stack.mExitingAppTokens);
-
-            final int exitingCount = mTmpExitingAppTokens.size();
-            for (int i = 0; i < exitingCount; i++) {
-                final AppWindowAnimator appAnimator = mTmpExitingAppTokens.get(i).mAppAnimator;
-                // stepAnimation can trigger finishExit->removeWindowInnerLocked
-                // ->performSurfacePlacement
-                // performSurfacePlacement will directly manipulate the mExitingAppTokens list
-                // so we need to iterate over a copy and check for modifications.
-                if (!stack.mExitingAppTokens.contains(appAnimator)) {
-                    continue;
-                }
-                appAnimator.wasAnimating = appAnimator.animating;
-                if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
-                    setAnimating(true);
-                    mAppWindowAnimating = true;
-                } else if (appAnimator.wasAnimating) {
-                    // stopped animating, do one more pass through the layout
-                    setAppLayoutChanges(appAnimator,
-                            WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                            "exiting appToken " + appAnimator.mAppToken + " done", displayId);
-                    if (DEBUG_ANIM) Slog.v(TAG,
-                            "updateWindowsApps...: done animating exiting "
-                                    + appAnimator.mAppToken);
-                }
-            }
+    /**
+     * @return The window that is currently hiding the Keyguard, or if it was hiding the Keyguard,
+     *         and it's still animating.
+     */
+    private WindowState getWinShowWhenLockedOrAnimating() {
+        final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+        if (winShowWhenLocked != null) {
+            return winShowWhenLocked;
         }
+        if (mLastShowWinWhenLocked != null && mLastShowWinWhenLocked.isOnScreen()
+                && mLastShowWinWhenLocked.isAnimatingLw()
+                && (mLastShowWinWhenLocked.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
+            return mLastShowWinWhenLocked;
+        }
+        return null;
     }
 
     private boolean shouldForceHide(WindowState win) {
@@ -227,18 +191,20 @@
                 ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
                         || !mPolicy.canBeForceHidden(imeTarget, imeTarget.mAttrs));
 
-        final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+        final WindowState winShowWhenLocked = getWinShowWhenLockedOrAnimating();
         final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
                 null : winShowWhenLocked.mAppToken;
 
         boolean allowWhenLocked = false;
         // Show IME over the keyguard if the target allows it
         allowWhenLocked |= (win.mIsImWindow || imeTarget == win) && showImeOverKeyguard;
-        // Show SHOW_WHEN_LOCKED windows
-        allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+        // Show SHOW_WHEN_LOCKED windows that turn on the screen
+        allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.mTurnOnScreen;
 
         if (appShowWhenLocked != null) {
             allowWhenLocked |= appShowWhenLocked == win.mAppToken
+                    // Show all SHOW_WHEN_LOCKED windows if some apps are shown over lockscreen
+                    || (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
                     // Show error dialogs over apps that are shown on lockscreen
                     || (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
         }
@@ -554,6 +520,11 @@
                 mPostKeyguardExitAnimation = null;
             }
         }
+
+        final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+        if (winShowWhenLocked != null) {
+            mLastShowWinWhenLocked = winShowWhenLocked;
+        }
     }
 
     private void updateWallpaperLocked(int displayId) {
@@ -621,54 +592,6 @@
         }
     }
 
-    /** See if any windows have been drawn, so they (and others associated with them) can now be
-     *  shown. */
-    private void testTokenMayBeDrawnLocked(int displayId) {
-        // See if any windows have been drawn, so they (and others
-        // associated with them) can now be shown.
-        final ArrayList<Task> tasks = mService.getDisplayContentLocked(displayId).getTasks();
-        final int numTasks = tasks.size();
-        for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
-            final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-            final int numTokens = tokens.size();
-            for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
-                final AppWindowToken wtoken = tokens.get(tokenNdx);
-                AppWindowAnimator appAnimator = wtoken.mAppAnimator;
-                final boolean allDrawn = wtoken.allDrawn;
-                if (allDrawn != appAnimator.allDrawn) {
-                    appAnimator.allDrawn = allDrawn;
-                    if (allDrawn) {
-                        // The token has now changed state to having all
-                        // windows shown...  what to do, what to do?
-                        if (appAnimator.freezingScreen) {
-                            appAnimator.showAllWindowsLocked();
-                            wtoken.stopFreezingScreen(false, true);
-                            if (DEBUG_ORIENTATION) Slog.i(TAG,
-                                    "Setting mOrientationChangeComplete=true because wtoken "
-                                    + wtoken + " numInteresting=" + wtoken.numInterestingWindows
-                                    + " numDrawn=" + wtoken.numDrawnWindows);
-                            // This will set mOrientationChangeComplete and cause a pass through
-                            // layout.
-                            setAppLayoutChanges(appAnimator,
-                                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                                    "testTokenMayBeDrawnLocked: freezingScreen", displayId);
-                        } else {
-                            setAppLayoutChanges(appAnimator,
-                                    WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
-                                    "testTokenMayBeDrawnLocked", displayId);
-
-                            // We can now show all of the drawn windows!
-                            if (!mService.mOpeningApps.contains(wtoken)) {
-                                orAnimating(appAnimator.showAllWindowsLocked());
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-
     /** Locked on mService.mWindowMap. */
     private void animateLocked(long frameTimeNs) {
         if (!mInitialized) {
@@ -686,13 +609,14 @@
 
         if (SHOW_TRANSACTIONS) Slog.i(
                 TAG, ">>> OPEN TRANSACTION animateLocked");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         SurfaceControl.setAnimationTransaction();
         try {
             final int numDisplays = mDisplayContentsAnimators.size();
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
-                updateAppWindowsLocked(displayId);
+                final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+                displayContent.stepAppWindowsAnimation(mCurrentTime);
                 DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
 
                 final ScreenRotationAnimation screenRotationAnimation =
@@ -730,8 +654,9 @@
 
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
 
-                testTokenMayBeDrawnLocked(displayId);
+                displayContent.checkAppWindowsReadyToShow();
 
                 final ScreenRotationAnimation screenRotationAnimation =
                         mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
@@ -739,12 +664,10 @@
                     screenRotationAnimation.updateSurfacesInTransaction();
                 }
 
-                orAnimating(mService.getDisplayContentLocked(displayId).animateDimLayers());
-                orAnimating(mService.getDisplayContentLocked(displayId).getDockedDividerController()
-                        .animate(mCurrentTime));
+                orAnimating(displayContent.animateDimLayers());
+                orAnimating(displayContent.getDockedDividerController().animate(mCurrentTime));
                 //TODO (multidisplay): Magnification is supported only for the default display.
-                if (mService.mAccessibilityController != null
-                        && displayId == Display.DEFAULT_DISPLAY) {
+                if (mService.mAccessibilityController != null && displayContent.isDefaultDisplay) {
                     mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
                 }
             }
@@ -763,7 +686,7 @@
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_TRANSACTIONS) Slog.i(
                     TAG, "<<< CLOSE TRANSACTION animateLocked");
         }
@@ -819,7 +742,7 @@
 
     private void removeReplacedWindowsLocked() {
         if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
                 DisplayContent display = mService.mDisplayContents.valueAt(i);
@@ -833,7 +756,7 @@
                 }
             }
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
         }
         mRemoveReplacedWindows = false;
@@ -923,11 +846,6 @@
         }
     }
 
-    void setAppLayoutChanges(
-            AppWindowAnimator appAnimator, int changes, String reason, int displayId) {
-        appAnimator.mAppToken.setAppLayoutChanges(changes, reason, displayId);
-    }
-
     private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
         DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
         if (displayAnimator == null) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fa05c0c..106284a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -17,29 +17,42 @@
 package com.android.server.wm;
 
 import android.annotation.CallSuper;
+import android.view.animation.Animation;
 
+import java.io.PrintWriter;
 import java.util.Comparator;
 import java.util.LinkedList;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
 /**
  * Defines common functionality for classes that can hold windows directly or through their
- * children.
+ * children in a hierarchy form.
  * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
  * changes are made to this class.
  */
-class WindowContainer {
+class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
 
     // The parent of this window container.
-    private WindowContainer mParent = null;
+    protected WindowContainer mParent = null;
 
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
-    protected final LinkedList<WindowContainer> mChildren = new LinkedList();
+    protected final LinkedList<E> mChildren = new LinkedList();
 
-    protected WindowContainer getParent() {
+    // The specified orientation for this window container.
+    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+    final protected WindowContainer getParent() {
         return mParent;
     }
 
+    // Temp. holders for a chain of containers we are currently processing.
+    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
+    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
+
     /**
      * Adds the input window container has a child of this container in order based on the input
      * comparator.
@@ -48,7 +61,12 @@
      *                   If null, the child will be added to the top.
      */
     @CallSuper
-    protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
+    protected void addChild(E child, Comparator<E> comparator) {
+        if (child.mParent != null) {
+            throw new IllegalArgumentException("addChild: container=" + child
+                    + " is already a child of container=" + child.mParent
+                    + " can't add to container=" + this);
+        }
         child.mParent = this;
 
         if (mChildren.isEmpty() || comparator == null) {
@@ -67,6 +85,33 @@
         mChildren.add(child);
     }
 
+    /** Adds the input window container has a child of this container at the input index. */
+    @CallSuper
+    protected void addChild(E child, int index) {
+        if (child.mParent != null) {
+            throw new IllegalArgumentException("addChild: container=" + child
+                    + " is already a child of container=" + child.mParent
+                    + " can't add to container=" + this);
+        }
+        child.mParent = this;
+        mChildren.add(index, child);
+    }
+
+    /**
+     * Removes the input child container from this container which is its parent.
+     *
+     * @return True if the container did contain the input child and it was detached.
+     */
+    @CallSuper
+    void removeChild(E child) {
+        if (mChildren.remove(child)) {
+            child.mParent = null;
+        } else {
+            throw new IllegalArgumentException("removeChild: container=" + child
+                    + " is not a child of container=" + this);
+        }
+    }
+
     /**
      * Removes this window container and its children with no regard for what else might be going on
      * in the system. For example, the container will be removed during animation if this method is
@@ -85,7 +130,7 @@
         }
 
         if (mParent != null) {
-            mParent.detachChild(this);
+            mParent.removeChild(this);
         }
     }
 
@@ -103,17 +148,6 @@
         }
     }
 
-    /** Detaches the input child container from this container which is its parent. */
-    @CallSuper
-    void detachChild(WindowContainer child) {
-        if (mChildren.remove(child)) {
-            child.mParent = null;
-        } else {
-            throw new IllegalArgumentException("detachChild: container=" + child
-                    + " is not a child of container=" + this);
-        }
-    }
-
     /** Returns true if this window container has the input child. */
     boolean hasChild(WindowContainer child) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
@@ -221,6 +255,10 @@
      * the container has any content to display.
      */
     boolean isVisible() {
+        // TODO: Will this be more correct if it checks the visibility of its parents?
+        // It depends...For example, Tasks and Stacks are only visible if there children are visible
+        // but, WindowState are not visible if there parent are not visible. Maybe have the
+        // container specify which direction to treverse for for visibility?
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
             if (wc.isVisible()) {
@@ -234,4 +272,223 @@
     WindowContainer getTop() {
         return mChildren.isEmpty() ? this : mChildren.peekLast();
     }
+
+    /** Returns true if there is still a removal being deferred */
+    boolean checkCompleteDeferredRemoval() {
+        boolean stillDeferringRemoval = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
+        }
+
+        return stillDeferringRemoval;
+    }
+
+    /** Checks if all windows in an app are all drawn and shows them if needed. */
+    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
+    // display. Remove once we migrate DisplayContent to use WindowContainer.
+    void checkAppWindowsReadyToShow(int displayId) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.checkAppWindowsReadyToShow(displayId);
+        }
+    }
+
+    /**
+     * Updates the current all drawn status for this container. That is all its children
+     * that should draw something have done so.
+     */
+    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
+    // display. Remove once we migrate DisplayContent to use WindowContainer.
+    void updateAllDrawn(int displayId) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.updateAllDrawn(displayId);
+        }
+    }
+
+    /** Step currently ongoing animation for App window containers. */
+    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
+    // display. Remove once we migrate DisplayContent to use WindowContainer.
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.stepAppWindowsAnimation(currentTime, displayId);
+        }
+    }
+
+    void onAppTransitionDone() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.onAppTransitionDone();
+        }
+    }
+
+    void overridePlayingAppAnimations(Animation a) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            mChildren.get(i).overridePlayingAppAnimations(a);
+        }
+    }
+
+    void setOrientation(int orientation) {
+        mOrientation = orientation;
+    }
+
+    /**
+     * Returns the specified orientation for this window container or one of its children is there
+     * is one set, or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSET} if no
+     * specification is set.
+     * NOTE: {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} is a
+     * specification...
+     */
+    int getOrientation() {
+
+        if (!fillsParent() || !isVisible()) {
+            // Ignore invisible containers or containers that don't completely fills their parents.
+            return SCREEN_ORIENTATION_UNSET;
+        }
+
+        // The container fills its parent so we can use it orientation if it has one specified,
+        // otherwise we prefer to use the orientation of its topmost child that has one
+        // specified and fall back on this container's unset or unspecified value as a candidate
+        // if none of the children have a better candidate for the orientation.
+        if (mOrientation != SCREEN_ORIENTATION_UNSET
+                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+            return mOrientation;
+        }
+        int candidate = mOrientation;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+
+            final int orientation = wc.getOrientation();
+            if (orientation == SCREEN_ORIENTATION_BEHIND) {
+                // container wants us to use the orientation of the container behind it. See if we
+                // can find one. Else return SCREEN_ORIENTATION_BEHIND so the caller can choose to
+                // look behind this container.
+                candidate = orientation;
+                continue;
+            }
+
+            if (orientation == SCREEN_ORIENTATION_UNSET) {
+                continue;
+            }
+
+            if (wc.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                // Use the orientation if the container fills its parent or requested an explicit
+                // orientation that isn't SCREEN_ORIENTATION_UNSPECIFIED.
+                return orientation;
+            }
+        }
+
+        return candidate;
+    }
+
+    /**
+     * Returns true if this container is opaque and fills all the space made available by its parent
+     * container.
+     *
+     * NOTE: It is possible for this container to occupy more space than the parent has (or less),
+     * this is just a signal from the client to window manager stating its intent, but not what it
+     * actually does.
+     */
+    boolean fillsParent() {
+        return false;
+    }
+
+    /**
+     * Rebuilds the WindowList for the input display content.
+     * @param dc The display content to rebuild the window list for.
+     * @param addIndex The index in the window list to add the next entry to.
+     * @return The next index in the window list to.
+     */
+    // TODO: Hoping we can get rid of WindowList so this method wouldn't be needed.
+    int rebuildWindowList(DisplayContent dc, int addIndex) {
+        final int count = mChildren.size();
+        for (int i = 0; i < count; i++) {
+            final WindowContainer wc = mChildren.get(i);
+            addIndex = wc.rebuildWindowList(dc, addIndex);
+        }
+        return addIndex;
+    }
+
+    /**
+     * Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
+     * the input container in terms of z-order.
+     */
+    @Override
+    public int compareTo(WindowContainer other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (mParent != null && mParent == other.mParent) {
+            final LinkedList<WindowContainer> list = mParent.mChildren;
+            return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
+        }
+
+        final LinkedList<WindowContainer> thisParentChain = mTmpChain1;
+        final LinkedList<WindowContainer> otherParentChain = mTmpChain2;
+        getParents(thisParentChain);
+        other.getParents(otherParentChain);
+
+        // Find the common ancestor of both containers.
+        WindowContainer commonAncestor = null;
+        WindowContainer thisTop = thisParentChain.peekLast();
+        WindowContainer otherTop = otherParentChain.peekLast();
+        while (thisTop != null && otherTop != null && thisTop == otherTop) {
+            commonAncestor = thisParentChain.removeLast();
+            otherParentChain.removeLast();
+            thisTop = thisParentChain.peekLast();
+            otherTop = otherParentChain.peekLast();
+        }
+
+        // Containers don't belong to the same hierarchy???
+        if (commonAncestor == null) {
+            throw new IllegalArgumentException("No in the same hierarchy this="
+                    + thisParentChain + " other=" + otherParentChain);
+        }
+
+        // Children are always considered greater than their parents, so if one of the containers
+        // we are comparing it the parent of the other then whichever is the child is greater.
+        if (commonAncestor == this) {
+            return -1;
+        } else if (commonAncestor == other) {
+            return 1;
+        }
+
+        // The position of the first non-common ancestor in the common ancestor list determines
+        // which is greater the which.
+        final LinkedList<WindowContainer> list = commonAncestor.mChildren;
+        return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
+                ? 1 : -1;
+    }
+
+    private void getParents(LinkedList<WindowContainer> parents) {
+        parents.clear();
+        WindowContainer current = this;
+        do {
+            parents.addLast(current);
+            current = current.mParent;
+        } while (current != null);
+    }
+
+    /**
+     * Dumps the names of this container children in the input print writer indenting each
+     * level with the input prefix.
+     */
+    void dumpChildrenNames(PrintWriter pw, String prefix) {
+        final String childPrefix = prefix + prefix;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            pw.println("#" + i + " " + getName());
+            wc.dumpChildrenNames(pw, childPrefix);
+        }
+    }
+
+    String getName() {
+        return toString();
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1cc7dad..09fd457 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -162,7 +162,6 @@
 import java.net.Socket;
 import java.text.DateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -170,7 +169,6 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 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.PINNED_STACK_ID;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -216,7 +214,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
@@ -497,12 +494,6 @@
     Runnable mWaitingForDrawnCallback;
 
     /**
-     * Used when rebuilding window list to keep track of windows that have
-     * been removed.
-     */
-    WindowState[] mRebuildTmp = new WindowState[20];
-
-    /**
      * Stores for each user whether screencapture is disabled
      * This array is essentially a cache for all userId for
      * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled}
@@ -607,7 +598,11 @@
     // State while inside of layoutAndPlaceSurfacesLocked().
     boolean mFocusMayChange;
 
-    Configuration mCurConfiguration = new Configuration();
+    /**
+     * Current global configuration information. Contains general settings for the entire system,
+     * corresponds to the configuration of the default display.
+     */
+    Configuration mGlobalConfiguration = new Configuration();
 
     // This is held as long as we have the screen frozen, to give us time to
     // perform a rotation animation when turning off shows the lock screen which
@@ -891,12 +886,30 @@
     // since they won't be notified through the app window animator.
     final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
 
-    // List of displays to reconfigure after configuration changes.
-    // Some of the information reported for a display is dependent on resources to do the right
-    // calculations. For example, {@link DisplayInfo#smallestNominalAppWidth} and company are
-    // dependent on the height and width of the status and nav bar which change depending on the
-    // current configuration.
-    private final DisplayContentList mReconfigureOnConfigurationChanged = new DisplayContentList();
+    // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
+    // instances will be replaced with an instance that writes a binary representation of all
+    // commands to mSurfaceTraceFd.
+    boolean mSurfaceTraceEnabled;
+    ParcelFileDescriptor mSurfaceTraceFd;
+    RemoteEventTrace mRemoteEventTrace;
+
+    void openSurfaceTransaction() {
+        synchronized (mWindowMap) {
+            if (mSurfaceTraceEnabled) {
+                mRemoteEventTrace.openSurfaceTransaction();
+            }
+            SurfaceControl.openTransaction();
+        }
+    }
+
+    void closeSurfaceTransaction() {
+        synchronized (mWindowMap) {
+            if (mSurfaceTraceEnabled) {
+                mRemoteEventTrace.closeSurfaceTransaction();
+            }
+            SurfaceControl.closeTransaction();
+        }
+    }
 
     /** Listener to notify activity manager about app transitions. */
     final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
@@ -933,6 +946,12 @@
         }
     };
 
+    final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>();
+
+    interface AppFreezeListener {
+        void onAppFreezeTimeout();
+    }
+
     public static WindowManagerService main(final Context context,
             final InputManagerService im,
             final boolean haveInputMethods, final boolean showBootMsgs,
@@ -1064,11 +1083,11 @@
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
 
-        SurfaceControl.openTransaction();
+        openSurfaceTransaction();
         try {
             createWatermarkInTransaction();
         } finally {
-            SurfaceControl.closeTransaction();
+            closeSurfaceTransaction();
         }
 
         showEmulatorDisplayOverlayIfNeeded();
@@ -1934,6 +1953,44 @@
         return false;
     }
 
+    @Override
+    public void enableSurfaceTrace(ParcelFileDescriptor pfd) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
+            throw new SecurityException("Only shell can call enableSurfaceTrace");
+        }
+        final FileDescriptor fd = pfd.getFileDescriptor();
+
+        synchronized (mWindowMap) {
+            if (mSurfaceTraceEnabled) {
+                disableSurfaceTrace();
+            }
+            mSurfaceTraceEnabled = true;
+            mRemoteEventTrace = new RemoteEventTrace(this, fd);
+            mSurfaceTraceFd = pfd;
+            for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+                DisplayContent dc = mDisplayContents.valueAt(displayNdx);
+                dc.enableSurfaceTrace(fd);
+            }
+        }
+    }
+
+    @Override
+    public void disableSurfaceTrace() {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID &&
+            callingUid != Process.SYSTEM_UID) {
+            throw new SecurityException("Only shell can call disableSurfaceTrace");
+        }
+        mSurfaceTraceEnabled = false;
+        mRemoteEventTrace = null;
+        mSurfaceTraceFd = null;
+        for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+            DisplayContent dc = mDisplayContents.valueAt(displayNdx);
+            dc.disableSurfaceTrace();
+        }
+    }
+
     /**
      * Set mScreenCaptureDisabled for specific user
      */
@@ -1972,7 +2029,7 @@
     /**
      * Performs some centralized bookkeeping clean-up on the window that is being removed.
      * NOTE: Should only be called from {@link WindowState#removeImmediately()}
-     * TODO: Maybe better handled with a method {@link WindowContainer#detachChild} if we can
+     * TODO: Maybe better handled with a method {@link WindowContainer#removeChild} if we can
      * figure-out a good way to have all parents of a WindowState doing the same thing without
      * forgetting to add the wiring when a new parent of WindowState is added.
      */
@@ -1997,7 +2054,7 @@
         final WindowToken token = win.mToken;
         final AppWindowToken atoken = win.mAppToken;
         if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing " + win + " from " + token);
-        token.removeWindow(win);
+        // Window will already be removed from token before this post clean-up method is called.
         if (token.isEmpty()) {
             if (!token.explicit) {
                 mTokenMap.remove(token.token);
@@ -2010,7 +2067,7 @@
         }
 
         if (atoken != null) {
-            atoken.removeWindow(win);
+            atoken.postWindowRemoveStartingWindowCleanup(win);
         }
 
         if (win.mAttrs.type == TYPE_WALLPAPER) {
@@ -2195,7 +2252,7 @@
                         Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild");
                     }
 
-                    SurfaceControl.openTransaction();
+                    openSurfaceTransaction();
 
                     try {
 
@@ -2209,7 +2266,7 @@
                         }
 
                     } finally {
-                        SurfaceControl.closeTransaction();
+                        closeSurfaceTransaction();
                         if (SHOW_TRANSACTIONS) {
                             Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild");
                         }
@@ -2698,9 +2755,10 @@
             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition."
                     + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
                     + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
-            Animation a = mAppTransition.loadAnimation(lp, transit, enter, mCurConfiguration.uiMode,
-                    mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets,
-                    isVoiceInteraction, freeform, atoken.mTask.mTaskId);
+            Animation a = mAppTransition.loadAnimation(lp, transit, enter,
+                    mGlobalConfiguration.uiMode, mGlobalConfiguration.orientation, frame,
+                    displayFrame, insets, surfaceInsets, isVoiceInteraction, freeform,
+                    atoken.mTask.mTaskId);
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken);
                 final int containingWidth = frame.width();
@@ -2791,7 +2849,7 @@
     }
 
     private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken,
-            Rect bounds, Configuration config, boolean isOnTopLauncher) {
+            Rect bounds, Configuration overrideConfig, boolean isOnTopLauncher) {
         if (DEBUG_STACK) Slog.i(TAG_WM, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId
                 + " atoken=" + atoken + " bounds=" + bounds);
         final TaskStack stack = mStackIdToStack.get(stackId);
@@ -2799,7 +2857,7 @@
             throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
         }
         EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
-        Task task = new Task(taskId, stack, userId, this, bounds, config, isOnTopLauncher);
+        Task task = new Task(taskId, stack, userId, this, bounds, overrideConfig, isOnTopLauncher);
         mTaskIdToTask.put(taskId, task);
         stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showForAllUsers);
         return task;
@@ -2809,9 +2867,9 @@
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
-            Rect taskBounds, Configuration config, int taskResizeMode, boolean alwaysFocusable,
-            boolean homeTask, int targetSdkVersion, int rotationAnimationHint,
-            boolean isOnTopLauncher) {
+            Rect taskBounds, Configuration overrideConfig, int taskResizeMode,
+            boolean alwaysFocusable, boolean homeTask, int targetSdkVersion,
+            int rotationAnimationHint, boolean isOnTopLauncher) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -2839,10 +2897,10 @@
             }
             atoken = new AppWindowToken(this, token, voiceInteraction);
             atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
-            atoken.appFullscreen = fullscreen;
+            atoken.setFillsParent(fullscreen);
             atoken.showForAllUsers = showForAllUsers;
             atoken.targetSdk = targetSdkVersion;
-            atoken.requestedOrientation = requestedOrientation;
+            atoken.setOrientation(requestedOrientation);
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
             atoken.mLaunchTaskBehind = launchTaskBehind;
@@ -2853,7 +2911,7 @@
 
             Task task = mTaskIdToTask.get(taskId);
             if (task == null) {
-                task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds, config,
+                task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds, overrideConfig,
                         isOnTopLauncher);
             }
             task.addAppToken(addPos, atoken, taskResizeMode, homeTask);
@@ -2866,7 +2924,8 @@
 
     @Override
     public void setAppTask(IBinder token, int taskId, int stackId, Rect taskBounds,
-            Configuration config, int taskResizeMode, boolean homeTask, boolean isOnTopLauncher) {
+            Configuration overrideConfig, int taskResizeMode, boolean homeTask,
+            boolean isOnTopLauncher) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppTask()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -2878,14 +2937,11 @@
                 Slog.w(TAG_WM, "Attempted to set task id of non-existing app token: " + token);
                 return;
             }
-            final Task oldTask = atoken.mTask;
-            oldTask.removeAppToken(atoken);
 
             Task newTask = mTaskIdToTask.get(taskId);
             if (newTask == null) {
-                newTask = createTaskLocked(
-                        taskId, stackId, oldTask.mUserId, atoken, taskBounds, config,
-                        isOnTopLauncher);
+                newTask = createTaskLocked(taskId, stackId, atoken.mTask.mUserId, atoken,
+                        taskBounds, overrideConfig, isOnTopLauncher);
             }
             newTask.addAppToken(Integer.MAX_VALUE /* at top */, atoken, taskResizeMode, homeTask);
         }
@@ -2947,7 +3003,7 @@
                 AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
                         null : winShowWhenLocked.mAppToken;
                 if (appShowWhenLocked != null) {
-                    int req = appShowWhenLocked.requestedOrientation;
+                    int req = appShowWhenLocked.getOrientation();
                     if (req == SCREEN_ORIENTATION_BEHIND) {
                         req = mLastKeyguardForcedOrientation;
                     }
@@ -2962,91 +3018,7 @@
         }
 
         // Top system windows are not requesting an orientation. Start searching from apps.
-        return getAppSpecifiedOrientation();
-    }
-
-    private int getAppSpecifiedOrientation() {
-        int lastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-        boolean findingBehind = false;
-        boolean lastFullscreen = false;
-        DisplayContent displayContent = getDefaultDisplayContentLocked();
-        final ArrayList<Task> tasks = displayContent.getTasks();
-        final boolean inMultiWindow = isStackVisibleLocked(DOCKED_STACK_ID)
-                || isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID);
-        final boolean dockMinimized =
-                getDefaultDisplayContentLocked().mDividerControllerLocked.isMinimizedDock();
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-            final int firstToken = tokens.size() - 1;
-            for (int tokenNdx = firstToken; tokenNdx >= 0; --tokenNdx) {
-                final AppWindowToken atoken = tokens.get(tokenNdx);
-
-                if (DEBUG_APP_ORIENTATION) Slog.v(TAG_WM, "Checking app orientation: " + atoken);
-
-                // if we're about to tear down this window and not seek for
-                // the behind activity, don't use it for orientation
-                if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Skipping " + atoken + " -- going to hide");
-                    continue;
-                }
-
-                if (tokenNdx == firstToken) {
-                    // If we have hit a new Task, and the bottom of the previous group didn't
-                    // explicitly say to use the orientation behind it, and the last app was
-                    // full screen, then we'll stick with the user's orientation.
-                    if (lastOrientation != SCREEN_ORIENTATION_BEHIND && lastFullscreen) {
-                        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Done at " + atoken
-                                + " -- end of group, return " + lastOrientation);
-                        return lastOrientation;
-                    }
-                }
-
-                // We ignore any hidden applications on the top.
-                if (atoken.hiddenRequested) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Skipping " + atoken + " -- hidden on top");
-                    continue;
-                }
-
-                // No app except the home app may specify the screen orientation in multi-window,
-                // and only if the docked stack is minimized to avoid weirdness when home task
-                // temporarily gets moved to the front.
-                if (inMultiWindow && (!atoken.mTask.isHomeTask() || !dockMinimized)) {
-                    continue;
-                }
-
-                if (tokenNdx == 0) {
-                    // Last token in this task.
-                    lastOrientation = atoken.requestedOrientation;
-                }
-
-                int or = atoken.requestedOrientation;
-                // If this application is fullscreen, and didn't explicitly say
-                // to use the orientation behind it, then just take whatever
-                // orientation it has and ignores whatever is under it.
-                lastFullscreen = atoken.appFullscreen;
-                if (lastFullscreen && or != SCREEN_ORIENTATION_BEHIND) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Done at " + atoken + " -- full screen, return " + or);
-                    return or;
-                }
-                // If this application has requested an explicit orientation, then use it.
-                if (or != SCREEN_ORIENTATION_UNSPECIFIED && or != SCREEN_ORIENTATION_BEHIND) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Done at " + atoken + " -- explicitly set, return " + or);
-                    return or;
-                }
-                findingBehind |= (or == SCREEN_ORIENTATION_BEHIND);
-            }
-        }
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                "No app is requesting an orientation, return " + mLastOrientation);
-        // The next app has not been requested to be visible, so we keep the current orientation
-        // to prevent freezing/unfreezing the display too early unless we are in multi-window, in
-        // which we don't let the app customize the orientation unless it was the home task that
-        // is handled above.
-        return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mLastOrientation;
+        return getDefaultDisplayContentLocked().getOrientation();
     }
 
     @Override
@@ -3086,11 +3058,12 @@
             config = computeNewConfigurationLocked();
 
         } else if (currentConfig != null) {
-            // No obvious action we need to take, but if our current
-            // state mismatches the activity manager's, update it,
-            // disregarding font scale, which should remain set to
-            // the value of the previous configuration.
-            mTempConfiguration.setToDefaults();
+            // No obvious action we need to take, but if our current state mismatches the activity
+            // manager's, update it, disregarding font scale, which should remain set to the value
+            // of the previous configuration.
+            // Here we're calling Configuration#unset() instead of setToDefaults() because we need
+            // to keep override configs clear of non-empty values (e.g. fontSize).
+            mTempConfiguration.unset();
             mTempConfiguration.updateFrom(currentConfig);
             computeScreenConfigurationLocked(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
@@ -3158,12 +3131,12 @@
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
-            boolean configChanged = mCurConfiguration.diff(config) != 0;
+            boolean configChanged = mGlobalConfiguration.diff(config) != 0;
             if (!configChanged) {
                 return null;
             }
             prepareFreezingAllTaskBounds();
-            mCurConfiguration = new Configuration(config);
+            mGlobalConfiguration = new Configuration(config);
             return onConfigurationChanged();
         }
     }
@@ -3188,30 +3161,19 @@
         }
 
     }
+
     private int[] onConfigurationChanged() {
         mPolicy.onConfigurationChanged();
 
-        final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
-        if (!mReconfigureOnConfigurationChanged.contains(defaultDisplayContent)) {
-            // The default display size information is heavily dependent on the resources in the
-            // current configuration, so we need to reconfigure it everytime the configuration
-            // changes. See {@link PhoneWindowManager#setInitialDisplaySize}...sigh...
-            mReconfigureOnConfigurationChanged.add(defaultDisplayContent);
-        }
-        for (int i = mReconfigureOnConfigurationChanged.size() - 1; i >= 0; i--) {
-            reconfigureDisplayLocked(mReconfigureOnConfigurationChanged.remove(i));
+        mChangedStackList.clear();
+
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
+            displayContent.onConfigurationChanged(mChangedStackList);
         }
 
-        defaultDisplayContent.getDockedDividerController().onConfigurationChanged();
-        mChangedStackList.clear();
-        for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
-            final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
-            if (stack.onConfigurationChanged()) {
-                mChangedStackList.add(stack.mStackId);
-            }
-        }
-        return mChangedStackList.isEmpty() ?
-                null : ArrayUtils.convertToIntArray(mChangedStackList);
+        return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
     }
 
     @Override
@@ -3222,13 +3184,13 @@
         }
 
         synchronized(mWindowMap) {
-            AppWindowToken atoken = findAppWindowToken(token.asBinder());
+            final AppWindowToken atoken = findAppWindowToken(token.asBinder());
             if (atoken == null) {
                 Slog.w(TAG_WM, "Attempted to set orientation of non-existing app token: " + token);
                 return;
             }
 
-            atoken.requestedOrientation = requestedOrientation;
+            atoken.setOrientation(requestedOrientation);
         }
     }
 
@@ -3240,7 +3202,7 @@
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             }
 
-            return wtoken.requestedOrientation;
+            return wtoken.getOrientation();
         }
     }
 
@@ -3556,9 +3518,9 @@
 
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
         synchronized (mWindowMap) {
-            AppWindowToken atoken = findAppWindowToken(token);
+            final AppWindowToken atoken = findAppWindowToken(token);
             if (atoken != null) {
-                atoken.appFullscreen = toOpaque;
+                atoken.setFillsParent(toOpaque);
                 setWindowOpaqueLocked(token, toOpaque);
                 mWindowPlacerLocked.requestTraversal();
             }
@@ -3722,8 +3684,7 @@
                 }
                 if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
                     // We're launchingBehind, add the launching activity to mOpeningApps.
-                    final WindowState win =
-                            findFocusedWindowLocked(getDefaultDisplayContentLocked());
+                    final WindowState win = getDefaultDisplayContentLocked().findFocusedWindow();
                     if (win != null) {
                         final AppWindowToken focusedToken = win.mAppToken;
                         if (focusedToken != null) {
@@ -3890,42 +3851,12 @@
         mH.sendMessage(m);
     }
 
-    void dumpAppTokensLocked() {
-        final int numStacks = mStackIdToStack.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
-            Slog.v(TAG_WM, "  Stack #" + stack.mStackId + " tasks from bottom to top:");
-            final ArrayList<Task> tasks = stack.getTasks();
-            final int numTasks = tasks.size();
-            for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                Slog.v(TAG_WM, "    Task #" + task.mTaskId + " activities from bottom to top:");
-                AppTokenList tokens = task.mAppTokens;
-                final int numTokens = tokens.size();
-                for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
-                    Slog.v(TAG_WM, "      activity #" + tokenNdx + ": " + tokens.get(tokenNdx).token);
-                }
-            }
-        }
-    }
-
-    void dumpWindowsLocked() {
-        final int numDisplays = mDisplayContents.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
-            Slog.v(TAG_WM, " Display #" + displayContent.getDisplayId());
-            final WindowList windows = displayContent.getWindowList();
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                Slog.v(TAG_WM, "  #" + winNdx + ": " + windows.get(winNdx));
-            }
-        }
-    }
-
-    void moveStackWindowsLocked(DisplayContent displayContent) {
+    /** Rebuilds the input display's window list and does a relayout if something changed. */
+    private void rebuildAppWindowsAndLayoutIfNeededLocked(DisplayContent displayContent) {
         final WindowList windows = displayContent.getWindowList();
         mTmpWindows.addAll(windows);
 
-        rebuildAppWindowListLocked(displayContent);
+        displayContent.rebuildAppWindowList();
 
         // Set displayContent.layoutNeeded if window order changed.
         final int tmpSize = mTmpWindows.size();
@@ -3963,7 +3894,6 @@
         mInputMonitor.setUpdateInputWindowsNeededLw();
         mWindowPlacerLocked.performSurfacePlacement();
         mInputMonitor.updateInputWindowsLw(false /*force*/);
-        //dump();
     }
 
     public void moveTaskToTop(int taskId) {
@@ -3990,7 +3920,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(false);
                 }
-                moveStackWindowsLocked(displayContent);
+                rebuildAppWindowsAndLayoutIfNeededLocked(displayContent);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4012,7 +3942,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(true);
                 }
-                moveStackWindowsLocked(stack.getDisplayContent());
+                rebuildAppWindowsAndLayoutIfNeededLocked(stack.getDisplayContent());
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4089,20 +4019,9 @@
         return null;
     }
 
-    void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
-        displayContent.detachStack(stack);
-        if (stack.detachFromDisplay()) {
-            mWindowPlacerLocked.requestTraversal();
-        }
-        if (stack.mStackId == DOCKED_STACK_ID) {
-            getDefaultDisplayContentLocked().mDividerControllerLocked
-                    .notifyDockedStackExistsChanged(false);
-        }
-    }
-
     public void detachStack(int stackId) {
         synchronized (mWindowMap) {
-            TaskStack stack = mStackIdToStack.get(stackId);
+            final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack != null) {
                 final DisplayContent displayContent = stack.getDisplayContent();
                 if (displayContent != null) {
@@ -4110,7 +4029,7 @@
                         stack.mDeferDetach = true;
                         return;
                     }
-                    detachStackLocked(displayContent, stack);
+                    displayContent.detachChild(stack);
                 }
             }
         }
@@ -4129,7 +4048,7 @@
                 if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + taskId);
                 return;
             }
-            task.removeLocked();
+            task.removeIfPossible();
         }
     }
 
@@ -4285,7 +4204,7 @@
      * Returns a {@link Configuration} object that contains configurations settings
      * that should be overridden due to the operation.
      */
-    public void resizeTask(int taskId, Rect bounds, Configuration configuration,
+    public void resizeTask(int taskId, Rect bounds, Configuration overrideConfig,
             boolean relayout, boolean forced) {
         synchronized (mWindowMap) {
             Task task = mTaskIdToTask.get(taskId);
@@ -4294,7 +4213,7 @@
                         + " not found.");
             }
 
-            if (task.resizeLocked(bounds, configuration, forced) && relayout) {
+            if (task.resizeLocked(bounds, overrideConfig, forced) && relayout) {
                 task.getDisplayContent().layoutNeeded = true;
                 mWindowPlacerLocked.performSurfacePlacement();
             }
@@ -4778,7 +4697,6 @@
             for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                 final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
                 displayContent.switchUserStacks();
-                rebuildAppWindowListLocked(displayContent);
             }
             mWindowPlacerLocked.performSurfacePlacement();
 
@@ -4798,22 +4716,13 @@
         }
     }
 
-    /**
-     * Returns whether there is a docked task for the current user.
-     */
+    /** Returns whether there is a docked task for the current user. */
     boolean hasDockedTasksForUser(int userId) {
         final TaskStack stack = mStackIdToStack.get(DOCKED_STACK_ID);
         if (stack == null) {
             return false;
         }
-
-        final ArrayList<Task> tasks = stack.getTasks();
-        boolean hasUserTask = false;
-        for (int i = tasks.size() - 1; i >= 0 && !hasUserTask; i--) {
-            final Task task = tasks.get(i);
-            hasUserTask = (task.mUserId == userId);
-        }
-        return hasUserTask;
+        return stack.hasTaskForUser(userId);
     }
 
     /* Called by WindowState */
@@ -5107,7 +5016,7 @@
 
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                     ">>> OPEN TRANSACTION showCircularMask(visible=" + visible + ")");
-            SurfaceControl.openTransaction();
+            openSurfaceTransaction();
             try {
                 if (visible) {
                     // TODO(multi-display): support multiple displays
@@ -5130,7 +5039,7 @@
                     mCircularDisplayMask = null;
                 }
             } finally {
-                SurfaceControl.closeTransaction();
+                closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                         "<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")");
             }
@@ -5142,7 +5051,7 @@
 
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                     ">>> OPEN TRANSACTION showEmulatorDisplayOverlay");
-            SurfaceControl.openTransaction();
+            openSurfaceTransaction();
             try {
                 if (mEmulatorDisplayOverlay == null) {
                     mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
@@ -5155,7 +5064,7 @@
                 }
                 mEmulatorDisplayOverlay.setVisibility(true);
             } finally {
-                SurfaceControl.closeTransaction();
+                closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                         "<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
             }
@@ -5197,6 +5106,8 @@
 
             if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM,
                     ">>> OPEN TRANSACTION showStrictModeViolation");
+            // TODO: Modify this to use the surface trace once it is not going crazy.
+            // b/31532461
             SurfaceControl.openTransaction();
             try {
                 // TODO(multi-display): support multiple displays
@@ -5830,14 +5741,14 @@
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
         // By updating the Display info here it will be available to
         // computeScreenConfigurationLocked later.
-        updateDisplayAndOrientationLocked(mCurConfiguration.uiMode);
+        updateDisplayAndOrientationLocked(mGlobalConfiguration.uiMode);
 
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         if (!inTransaction) {
             if (SHOW_TRANSACTIONS) {
                 Slog.i(TAG_WM, ">>> OPEN TRANSACTION setRotationUnchecked");
             }
-            SurfaceControl.openTransaction();
+            openSurfaceTransaction();
         }
         try {
             // NOTE: We disable the rotation in the emulator because
@@ -5862,7 +5773,7 @@
             mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
         } finally {
             if (!inTransaction) {
-                SurfaceControl.closeTransaction();
+                closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS) {
                     Slog.i(TAG_WM, "<<< CLOSE TRANSACTION setRotationUnchecked");
                 }
@@ -6380,9 +6291,9 @@
         return null;
     }
 
-    /*
-     * Instruct the Activity Manager to fetch the current configuration and broadcast
-     * that to config-changed listeners if appropriate.
+    /**
+     * Instruct the Activity Manager to fetch new configurations, update global configuration
+     * and broadcast changes to config-changed listeners if appropriate.
      */
     void sendNewConfiguration() {
         try {
@@ -7505,20 +7416,8 @@
                     synchronized (mWindowMap) {
                         Slog.w(TAG_WM, "App freeze timeout expired.");
                         mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
-                        final int numStacks = mStackIdToStack.size();
-                        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                            final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
-                            final ArrayList<Task> tasks = stack.getTasks();
-                            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                                AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                                    AppWindowToken tok = tokens.get(tokenNdx);
-                                    if (tok.mAppAnimator.freezingScreen) {
-                                        Slog.w(TAG_WM, "Force clearing freeze: " + tok);
-                                        tok.stopFreezingScreen(true, true);
-                                    }
-                                }
-                            }
+                        for (int i = mAppFreezeListeners.size() - 1; i >=0 ; --i) {
+                            mAppFreezeListeners.get(i).onAppFreezeTimeout();
                         }
                     }
                     break;
@@ -8213,9 +8112,7 @@
         reconfigureDisplayLocked(displayContent);
     }
 
-    // displayContent must not be null
-    private void reconfigureDisplayLocked(DisplayContent displayContent) {
-        // TODO: Multidisplay: for now only use with default display.
+    void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) {
         if (!mDisplayReady) {
             return;
         }
@@ -8223,18 +8120,14 @@
         displayContent.layoutNeeded = true;
 
         boolean configChanged = updateOrientationFromAppTokensLocked(false);
-        mTempConfiguration.setToDefaults();
-        mTempConfiguration.updateFrom(mCurConfiguration);
+        mTempConfiguration.setTo(mGlobalConfiguration);
         computeScreenConfigurationLocked(mTempConfiguration);
-        configChanged |= mCurConfiguration.diff(mTempConfiguration) != 0;
+        configChanged |= mGlobalConfiguration.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
             mWaitingForConfig = true;
             startFreezingDisplayLocked(false, 0, 0);
             mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
-            if (!mReconfigureOnConfigurationChanged.contains(displayContent)) {
-                mReconfigureOnConfigurationChanged.add(displayContent);
-            }
         }
 
         mWindowPlacerLocked.performSurfacePlacement();
@@ -8322,102 +8215,6 @@
         return win;
     }
 
-    final void rebuildAppWindowListLocked() {
-        rebuildAppWindowListLocked(getDefaultDisplayContentLocked());
-    }
-
-    private void rebuildAppWindowListLocked(final DisplayContent displayContent) {
-        final WindowList windows = displayContent.getWindowList();
-        int NW = windows.size();
-        int i;
-        int lastBelow = -1;
-        int numRemoved = 0;
-
-        if (mRebuildTmp.length < NW) {
-            mRebuildTmp = new WindowState[NW+10];
-        }
-
-        // First remove all existing app windows.
-        i=0;
-        while (i < NW) {
-            WindowState w = windows.get(i);
-            if (w.mAppToken != null) {
-                WindowState win = windows.remove(i);
-                win.mRebuilding = true;
-                mRebuildTmp[numRemoved] = win;
-                mWindowsChanged = true;
-                if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Rebuild removing window: " + win);
-                NW--;
-                numRemoved++;
-                continue;
-            } else if (lastBelow == i-1) {
-                if (w.mAttrs.type == TYPE_WALLPAPER) {
-                    lastBelow = i;
-                }
-            }
-            i++;
-        }
-
-        // Keep whatever windows were below the app windows still below, by skipping them.
-        lastBelow++;
-        i = lastBelow;
-
-        // First add all of the exiting app tokens...  these are no longer in the main app list,
-        // but still have windows shown. We put them in the back because now that the animation is
-        // over we no longer will care about them.
-        final ArrayList<TaskStack> stacks = displayContent.getStacks();
-        final int numStacks = stacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            AppTokenList exitingAppTokens = stacks.get(stackNdx).mExitingAppTokens;
-            int NT = exitingAppTokens.size();
-            for (int j = 0; j < NT; j++) {
-                i = exitingAppTokens.get(j).reAddAppWindows(displayContent, i);
-            }
-        }
-
-        // And add in the still active app tokens in Z order.
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks();
-            final int numTasks = tasks.size();
-            for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
-                final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                final int numTokens = tokens.size();
-                for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
-                    final AppWindowToken wtoken = tokens.get(tokenNdx);
-                    if (wtoken.mIsExiting && !wtoken.waitingForReplacement()) {
-                        continue;
-                    }
-                    i = wtoken.reAddAppWindows(displayContent, i);
-                }
-            }
-        }
-
-        i -= lastBelow;
-        if (i != numRemoved) {
-            displayContent.layoutNeeded = true;
-            Slog.w(TAG_WM, "On display=" + displayContent.getDisplayId() + " Rebuild removed "
-                    + numRemoved + " windows but added " + i + " rebuildAppWindowListLocked() "
-                    + " callers=" + Debug.getCallers(10));
-            for (i = 0; i < numRemoved; i++) {
-                WindowState ws = mRebuildTmp[i];
-                if (ws.mRebuilding) {
-                    StringWriter sw = new StringWriter();
-                    PrintWriter pw = new FastPrintWriter(sw, false, 1024);
-                    ws.dump(pw, "", true);
-                    pw.flush();
-                    Slog.w(TAG_WM, "This window was lost: " + ws);
-                    Slog.w(TAG_WM, sw.toString());
-                    ws.mWinAnimator.destroySurfaceLocked();
-                }
-            }
-            Slog.w(TAG_WM, "Current app token list:");
-            dumpAppTokensLocked();
-            Slog.w(TAG_WM, "Final window list:");
-            dumpWindowsLocked();
-        }
-        Arrays.fill(mRebuildTmp, null);
-    }
-
     void makeWindowFreezingScreenIfNeededLocked(WindowState w) {
         // If the screen is currently frozen or off, then keep
         // it frozen/off until this window draws at its new
@@ -8454,27 +8251,15 @@
 
         mWallpaperControllerLocked.hideDeferredWallpapersIfNeeded();
 
-        // Restore window app tokens to the ActivityManager views
-        ArrayList<TaskStack> stacks = getDefaultDisplayContentLocked().getStacks();
-        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                    tokens.get(tokenNdx).sendingToBottom = false;
-                }
-            }
-        }
-        rebuildAppWindowListLocked();
+        getDefaultDisplayContentLocked().onAppTransitionDone();
 
         changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM,
                 "Wallpaper layer changed: assigning layers + relayout");
         moveInputMethodWindowsIfNeededLocked(true);
         mWindowPlacerLocked.mWallpaperMayChange = true;
-        // Since the window list has been rebuilt, focus might
-        // have to be recomputed since the actual order of windows
-        // might have changed again.
+        // Since the window list has been rebuilt, focus might have to be recomputed since the
+        // actual order of windows might have changed again.
         mFocusMayChange = true;
 
         return changes;
@@ -8484,30 +8269,25 @@
         final WindowStateAnimator winAnimator = w.mWinAnimator;
         if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq && !w.isGoneForLayoutLw()) {
             final Task task = w.getTask();
-            // In the case of stack bound animations, the window frames
-            // will update (unlike other animations which just modifiy
-            // various transformation properties). We don't want to
-            // notify the client of frame changes in this case. Not only
-            // is it a lot of churn, but the frame may not correspond
-            // to the surface size or the onscreen area at various
-            // phases in the animation, and the client will become
-            // sad and confused.
+            // In the case of stack bound animations, the window frames will update (unlike other
+            // animations which just modify various transformation properties). We don't want to
+            // notify the client of frame changes in this case. Not only is it a lot of churn, but
+            // the frame may not correspond to the surface size or the onscreen area at various
+            // phases in the animation, and the client will become sad and confused.
             if (task != null && task.mStack.getBoundsAnimating()) {
                 return;
             }
             w.setReportResizeHints();
             boolean configChanged = w.isConfigChanged();
             if (DEBUG_CONFIGURATION && configChanged) {
-                Slog.v(TAG_WM, "Win " + w + " config changed: "
-                        + mCurConfiguration);
+                Slog.v(TAG_WM, "Win " + w + " config changed: " + mGlobalConfiguration);
             }
             final boolean dragResizingChanged = w.isDragResizeChanged()
                     && !w.isDragResizingChangeReported();
 
-            if (localLOGV) Slog.v(TAG_WM, "Resizing " + w
-                    + ": configChanged=" + configChanged
-                    + " dragResizingChanged=" + dragResizingChanged
-                    + " last=" + w.mLastFrame + " frame=" + w.mFrame);
+            if (localLOGV) Slog.v(TAG_WM, "Resizing " + w + ": configChanged=" + configChanged
+                    + " dragResizingChanged=" + dragResizingChanged + " last=" + w.mLastFrame
+                    + " frame=" + w.mFrame);
 
             // We update mLastFrame always rather than in the conditional with the
             // last inset variables, because mFrameSizeChanged only tracks the
@@ -8857,7 +8637,7 @@
         final int displayCount = mDisplayContents.size();
         for (int i = 0; i < displayCount; i++) {
             final DisplayContent displayContent = mDisplayContents.valueAt(i);
-            WindowState win = findFocusedWindowLocked(displayContent);
+            final WindowState win = displayContent.findFocusedWindow();
             if (win != null) {
                 return win;
             }
@@ -8865,67 +8645,6 @@
         return null;
     }
 
-    WindowState findFocusedWindowLocked(DisplayContent displayContent) {
-        final WindowList windows = displayContent.getWindowList();
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
-
-            if (localLOGV || DEBUG_FOCUS) Slog.v(
-                TAG_WM, "Looking for focus: " + i
-                + " = " + win
-                + ", flags=" + win.mAttrs.flags
-                + ", canReceive=" + win.canReceiveKeys());
-
-            if (!win.canReceiveKeys()) {
-                continue;
-            }
-
-            AppWindowToken wtoken = win.mAppToken;
-
-            // If this window's application has been removed, just skip it.
-            if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) {
-                if (DEBUG_FOCUS) Slog.v(TAG_WM, "Skipping " + wtoken + " because "
-                        + (wtoken.removed ? "removed" : "sendingToBottom"));
-                continue;
-            }
-
-            // Descend through all of the app tokens and find the first that either matches
-            // win.mAppToken (return win) or mFocusedApp (return null).
-            if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING &&
-                    mFocusedApp != null) {
-                ArrayList<Task> tasks = displayContent.getTasks();
-                for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                    AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                    int tokenNdx = tokens.size() - 1;
-                    for ( ; tokenNdx >= 0; --tokenNdx) {
-                        final AppWindowToken token = tokens.get(tokenNdx);
-                        if (wtoken == token) {
-                            break;
-                        }
-                        if (mFocusedApp == token && token.windowsAreFocusable()) {
-                            // Whoops, we are below the focused app whose windows are focusable...
-                            // No focus for you!!!
-                            if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM,
-                                    "findFocusedWindow: Reached focused app=" + mFocusedApp);
-                            return null;
-                        }
-                    }
-                    if (tokenNdx >= 0) {
-                        // Early exit from loop, must have found the matching token.
-                        break;
-                    }
-                }
-            }
-
-            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ " + i +
-                        " = " + win);
-            return win;
-        }
-
-        if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows.");
-        return null;
-    }
-
     void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) {
         if (mDisplayFrozen) {
             return;
@@ -8989,7 +8708,7 @@
             // TODO(multidisplay): rotation on main screen only.
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure);
+                    mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure, this);
             mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
         }
     }
@@ -9127,6 +8846,7 @@
             if (line != null) {
                 String[] toks = line.split("%");
                 if (toks != null && toks.length > 0) {
+                    // TODO(multi-display): Show watermarks on secondary displays.
                     mWatermark = new Watermark(getDefaultDisplayContentLocked().getDisplay(),
                             mRealDisplayMetrics, mFxSession, toks);
                 }
@@ -9568,7 +9288,7 @@
             }
         }
         pw.println();
-        pw.print("  mCurConfiguration="); pw.println(this.mCurConfiguration);
+        pw.print("  mGlobalConfiguration="); pw.println(mGlobalConfiguration);
         pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
         pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
         if (mLastFocus != mCurrentFocus) {
@@ -10623,4 +10343,15 @@
             }
         }
     }
+
+    void registerAppFreezeListener(AppFreezeListener listener) {
+        if (!mAppFreezeListeners.contains(listener)) {
+            mAppFreezeListeners.add(listener);
+        }
+    }
+
+    void unregisterAppFreezeListener(AppFreezeListener listener) {
+        mAppFreezeListeners.remove(listener);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fccd2d9..60b0d03 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -141,10 +141,8 @@
     }
 }
 
-/**
- * A window in the window manager.
- */
-class WindowState extends WindowContainer implements WindowManagerPolicy.WindowState {
+/** A window in the window manager. */
+class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;
 
     // The minimal size of a window within the usable area of the freeform stack.
@@ -551,9 +549,9 @@
      * Compares to window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
      */
-    private static final Comparator<WindowContainer> sWindowSubLayerComparator = (w1, w2) -> {
-        final int layer1 = ((WindowState)w1).mSubLayer;
-        final int layer2 = ((WindowState)w2).mSubLayer;
+    private static final Comparator<WindowState> sWindowSubLayerComparator = (w1, w2) -> {
+        final int layer1 = w1.mSubLayer;
+        final int layer2 = w2.mSubLayer;
         if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
             // We insert the child window into the list ordered by the sub-layer.
             // For same sub-layers, the negative one should go below others; the positive one should
@@ -1202,7 +1200,7 @@
             // has been hidden.
             return true;
         }
-        return super.isVisible();
+        return false;
     }
 
     /**
@@ -1449,7 +1447,7 @@
         boolean changed = false;
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             changed |= c.onAppVisibilityChanged(visible, runningAppAnimation);
         }
 
@@ -1500,7 +1498,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             changed |= c.onSetAppExiting();
         }
 
@@ -1543,7 +1541,7 @@
 
     void onUnfreezeBounds() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.onUnfreezeBounds();
         }
 
@@ -1571,7 +1569,7 @@
 
     boolean isObscuringFullscreen(final DisplayInfo displayInfo) {
         Task task = getTask();
-        if (task != null && task.mStack != null && !task.mStack.isFullscreen()) {
+        if (task != null && task.mStack != null && !task.mStack.fillsParent()) {
             return false;
         }
         if (!isOpaqueDrawn() || !isFrameFullscreen(displayInfo)) {
@@ -1588,7 +1586,7 @@
     boolean isConfigChanged() {
         getMergedConfig(mTmpConfig);
 
-        // If the merged configuration is still empty, it means that we haven't issues the
+        // If the merged configuration is still empty, it means that we haven't issued the
         // configuration to the client yet and we need to return true so the configuration updates.
         boolean configChanged = mMergedConfiguration.equals(Configuration.EMPTY)
                 || mTmpConfig.diff(mMergedConfiguration) != 0;
@@ -1618,7 +1616,7 @@
             removeImmediately();
         } else {
             for (int i = mChildren.size() - 1; i >= 0; --i) {
-                final WindowState c = (WindowState) mChildren.get(i);
+                final WindowState c = mChildren.get(i);
                 c.onWindowReplacementTimeout();
             }
         }
@@ -1891,7 +1889,7 @@
         mJustMovedInStack = true;
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.notifyMovedInStack();
         }
     }
@@ -1912,7 +1910,7 @@
         mJustMovedInStack = false;
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.resetJustMovedInStack();
         }
     }
@@ -1935,7 +1933,7 @@
         if (mInputChannel != null) {
             throw new IllegalStateException("Window already has an input channel.");
         }
-        String name = makeInputChannelName();
+        String name = getName();
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
         mInputChannel = inputChannels[0];
         mClientChannel = inputChannels[1];
@@ -2007,7 +2005,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             if (c.removeReplacedWindowIfNeeded(replacement)) {
                 return true;
             }
@@ -2041,7 +2039,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             replacementSet |= c.setReplacementWindowIfNeeded(replacementCandidate);
         }
 
@@ -2414,7 +2412,7 @@
             return true;
         }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             if (c.isAnimatingInvisibleWithSavedSurface()) {
                 return true;
             }
@@ -2424,7 +2422,7 @@
 
     void stopUsingSavedSurface() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.stopUsingSavedSurface();
         }
 
@@ -2445,7 +2443,7 @@
             mWinAnimator.mAnimating = true;
         }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.markSavedSurfaceExiting();
         }
     }
@@ -2454,7 +2452,7 @@
         animators.add(mWinAnimator);
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.addWinAnimatorToList(animators);
         }
     }
@@ -2486,7 +2484,7 @@
     public void clearWasVisibleBeforeClientHidden() {
         mWasVisibleBeforeClientHidden = false;
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.clearWasVisibleBeforeClientHidden();
         }
     }
@@ -2498,7 +2496,7 @@
     void onStartFreezingScreen() {
         mAppFreezing = true;
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.onStartFreezingScreen();
         }
     }
@@ -2506,7 +2504,7 @@
     boolean onStopFreezingScreen() {
         boolean unfrozeWindows = false;
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             unfrozeWindows |= c.onStopFreezingScreen();
         }
 
@@ -2576,7 +2574,7 @@
     boolean destroySurface(boolean cleanupOnResume, boolean appStopped) {
         boolean destroyedSomething = false;
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             destroyedSomething |= c.destroySurface(cleanupOnResume, appStopped);
         }
 
@@ -2636,7 +2634,7 @@
 
     void destroySavedSurface() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.destroySavedSurface();
         }
 
@@ -2652,7 +2650,7 @@
     int restoreSavedSurfaceForInterestingWindow() {
         int interestingNotDrawn = -1;
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             final int childInterestingNotDrawn = c.restoreSavedSurfaceForInterestingWindow();
             if (childInterestingNotDrawn != -1) {
                 if (interestingNotDrawn == -1) {
@@ -2723,7 +2721,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             if (c.canRestoreSurface()) {
                 return true;
             }
@@ -2843,6 +2841,8 @@
         region.op(mTmpRect, Region.Op.INTERSECT);
     }
 
+    // TODO: This is one reason why WindowList are bad...prime candidate for removal once we
+    // figure-out a good way to replace WindowList with WindowContainer hierarchy.
     WindowList getWindowList() {
         final DisplayContent displayContent = getDisplayContent();
         return displayContent == null ? null : displayContent.getWindowList();
@@ -2899,8 +2899,7 @@
         final Configuration overrideConfig = task != null
                 ? task.mOverrideConfig
                 : Configuration.EMPTY;
-        final Configuration serviceConfig = mService.mCurConfiguration;
-        outConfig.setTo(serviceConfig);
+        outConfig.setTo(mService.mGlobalConfiguration);
         if (overrideConfig != Configuration.EMPTY) {
             outConfig.updateFrom(overrideConfig);
         }
@@ -3329,7 +3328,8 @@
         }
     }
 
-    String makeInputChannelName() {
+    @Override
+    String getName() {
         return Integer.toHexString(System.identityHashCode(this))
             + " " + getWindowTag();
     }
@@ -3461,8 +3461,8 @@
     WindowState getBottomChild() {
         // Child windows are z-ordered based on sub-layer using {@link #sWindowSubLayerComparator}
         // and the child with the lowest z-order will be at the head of the list.
-        WindowContainer c = mChildren.peekFirst();
-        return c == null ? null : (WindowState)c;
+        WindowState c = mChildren.peekFirst();
+        return c == null ? null : c;
     }
 
     boolean layoutInParentFrame() {
@@ -3493,7 +3493,7 @@
 
     void setWillReplaceWindow(boolean animate) {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.setWillReplaceWindow(animate);
         }
 
@@ -3515,7 +3515,7 @@
         mAnimateReplacingWindow = false;
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.clearWillReplaceWindow();
         }
     }
@@ -3526,7 +3526,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             if (c.waitingForReplacement()) {
                 return true;
             }
@@ -3542,7 +3542,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.requestUpdateWallpaperIfNeeded();
         }
     }
@@ -3590,7 +3590,7 @@
             setWillReplaceWindow(false /* animate */);
         }
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.setWillReplaceChildWindows();
         }
     }
@@ -3600,7 +3600,7 @@
             return this;
         }
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             final WindowState replacing = c.getReplacingWindow();
             if (replacing != null) {
                 return replacing;
@@ -3648,7 +3648,7 @@
             final DisplayContent displayContent = getDisplayContent();
 
             for (int i = mChildren.size() - 1; i >= 0; --i) {
-                final WindowState c = (WindowState) mChildren.get(i);
+                final WindowState c = mChildren.get(i);
                 if (c.mWinAnimator.mSurfaceController != null) {
                     c.performShowLocked();
                     // It hadn't been shown, which means layout not performed on it, so now we
@@ -3710,7 +3710,7 @@
                 windowInfo.childTokens = new ArrayList(childCount);
             }
             for (int j = 0; j < childCount; j++) {
-                final WindowState child = (WindowState) mChildren.get(j);
+                final WindowState child = mChildren.get(j);
                 windowInfo.childTokens.add(child.mClient.asBinder());
             }
         }
@@ -3720,7 +3720,7 @@
     int getHighestAnimLayer() {
         int highest = mWinAnimator.mAnimLayer;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             final int childLayer = c.getHighestAnimLayer();
             if (childLayer > highest) {
                 highest = childLayer;
@@ -3734,7 +3734,7 @@
         if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG_WM,
                 "adjustAnimLayer win=" + this + " anim layer: " + mWinAnimator.mAnimLayer);
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState childWindow = (WindowState) mChildren.get(i);
+            final WindowState childWindow = mChildren.get(i);
             childWindow.adjustAnimLayer(adj);
             if (childWindow.mWinAnimator.mAnimLayer > highestAnimLayer) {
                 highestAnimLayer = childWindow.mWinAnimator.mAnimLayer;
@@ -3743,6 +3743,16 @@
         return highestAnimLayer;
     }
 
+    @Override
+    int rebuildWindowList(DisplayContent dc, int addIndex) {
+        final DisplayContent winDisplayContent = getDisplayContent();
+        if (winDisplayContent == dc || winDisplayContent == null) {
+            mDisplayContent = dc;
+            return reAddWindow(addIndex);
+        }
+        return addIndex;
+    }
+
     // TODO: come-up with a better name for this method that represents what it does.
     // Or, it is probably not going to matter anyways if we are successful in getting rid of
     // the WindowList concept.
@@ -3753,7 +3763,7 @@
         final int childCount = mChildren.size();
         boolean winAdded = false;
         for (int j = 0; j < childCount; j++) {
-            final WindowState child = (WindowState) mChildren.get(j);
+            final WindowState child = mChildren.get(j);
             if (!winAdded && child.mSubLayer >= 0) {
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
                         "Re-adding child window at " + index + ": " + child);
@@ -3791,7 +3801,7 @@
         int childCount = mChildren.size();
         while (childCount > 0) {
             childCount--;
-            final WindowState cw = (WindowState) mChildren.get(childCount);
+            final WindowState cw = mChildren.get(childCount);
             int cpos = windows.indexOf(cw);
             if (cpos >= 0) {
                 if (cpos < interestingPos) interestingPos--;
@@ -3808,7 +3818,7 @@
             return true;
         }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             if (c.isWindowAnimationSet()) {
                 return true;
             }
@@ -3824,9 +3834,9 @@
         if (!mChildren.isEmpty()) {
             // Copying to a different list as multiple children can be removed.
             // TODO: Not sure if we really need to copy this into a different list.
-            final LinkedList childWindows = new LinkedList(mChildren);
+            final LinkedList<WindowState> childWindows = new LinkedList(mChildren);
             for (int i = childWindows.size() - 1; i >= 0; i--) {
-                ((WindowState)childWindows.get(i)).onExitAnimationDone();
+                childWindows.get(i).onExitAnimationDone();
             }
         }
 
@@ -3920,7 +3930,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            didSomething |= ((WindowState) mChildren.get(i)).clearAnimatingFlags();
+            didSomething |= (mChildren.get(i)).clearAnimatingFlags();
         }
 
         return didSomething;
@@ -3932,7 +3942,7 @@
 
     void hideWallpaperWindow(boolean wasDeferred, String reason) {
         for (int j = mChildren.size() - 1; j >= 0; --j) {
-            final WindowState c = (WindowState) mChildren.get(j);
+            final WindowState c = mChildren.get(j);
             c.hideWallpaperWindow(wasDeferred, reason);
         }
         if (!mWinAnimator.mLastHidden || wasDeferred) {
@@ -3972,7 +3982,7 @@
             return true;
         }
         for (int j = mChildren.size() - 1; j >= 0; --j) {
-            final WindowState c = (WindowState) mChildren.get(j);
+            final WindowState c = mChildren.get(j);
             if (c.hasVisibleNotDrawnWallpaper()) {
                 return true;
             }
@@ -3980,63 +3990,9 @@
         return false;
     }
 
-    /** Places this window after the input window in the window list. */
-    void addWindowToListAfter(WindowState pos) {
-        final WindowList windows = pos.getWindowList();
-        final int i = windows.indexOf(pos);
-        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                "Adding window " + this + " at " + (i+1) + " of " + windows.size()
-                + " (after " + pos + ")");
-        windows.add(i+1, this);
-        mService.mWindowsChanged = true;
-    }
-
-    /** Places this window before the input window in the window list. */
-    void addWindowToListBefore(WindowState pos) {
-        final WindowList windows = pos.getWindowList();
-        int i = windows.indexOf(pos);
-        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                "Adding window " + this + " at " + i + " of " + windows.size()
-                + " (before " + pos + ")");
-        if (i < 0) {
-            Slog.w(TAG_WM, "addWindowToListBefore: Unable to find " + pos + " in " + windows);
-            i = 0;
-        }
-        windows.add(i, this);
-        mService.mWindowsChanged = true;
-    }
-
-    /** Adds this non-app window to the window list. */
-    void addNonAppWindowToList() {
-        final WindowList windows = getWindowList();
-
-        // Figure out where window should go, based on layer.
-        int i;
-        for (i = windows.size() - 1; i >= 0; i--) {
-            final WindowState otherWin = windows.get(i);
-            if (otherWin.getBaseType() != TYPE_WALLPAPER && otherWin.mBaseLayer <= mBaseLayer) {
-                // Wallpaper wanders through the window list, for example to position itself
-                // directly behind keyguard. Because of this it will break the ordering based on
-                // WindowState.mBaseLayer. There might windows with higher mBaseLayer behind it and
-                // we don't want the new window to appear above them. An example of this is adding
-                // of the docked stack divider. Consider a scenario with the following ordering (top
-                // to bottom): keyguard, wallpaper, assist preview, apps. We want the dock divider
-                // to land below the assist preview, so the dock divider must ignore the wallpaper,
-                // with which it shares the base layer.
-                break;
-            }
-        }
-
-        i++;
-        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                "Free window: Adding window " + this + " at " + i + " of " + windows.size());
-        windows.add(i, this);
-        mService.mWindowsChanged = true;
-    }
-
     void updateReportedVisibility(UpdateReportedVisibilityResults results) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState c = mChildren.get(i);
             c.updateReportedVisibility(results);
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b9956c8..125368c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -70,6 +70,7 @@
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
+import java.io.FileDescriptor;
 
 /**
  * Keep track of animations and surface operations for a single WindowState.
@@ -225,8 +226,6 @@
     int mAttrType;
 
     static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
-    long mDeferTransactionUntilFrame = -1;
-    long mDeferTransactionTime = -1;
 
     boolean mForceScaleUntilResize;
 
@@ -1582,7 +1581,7 @@
 
         try {
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
             mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
                     mWin.mFrame.top + top, false);
             calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
@@ -1591,7 +1590,7 @@
             Slog.w(TAG, "Error positioning surface of " + mWin
                     + " pos=(" + left + "," + top + ")", e);
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION setWallpaperOffset");
         }
@@ -1889,35 +1888,10 @@
         if (!mWin.isChildWindow()) {
             return;
         }
-        mDeferTransactionUntilFrame = frameNumber;
-        mDeferTransactionTime = System.currentTimeMillis();
         mSurfaceController.deferTransactionUntil(
                 mWin.getParentWindow().mWinAnimator.mSurfaceController.getHandle(), frameNumber);
     }
 
-    // Defer the current transaction to the frame number of the last saved transaction.
-    // We do this to avoid shooting through an unsynchronized transaction while something is
-    // pending. This is generally fine, as either we will get in on the synchronization,
-    // or SurfaceFlinger will see that the frame has already occured. The only
-    // potential problem is in frame number resets so we reset things with a timeout
-    // every so often to be careful.
-    void deferToPendingTransaction() {
-        if (mDeferTransactionUntilFrame < 0) {
-            return;
-        }
-        final WindowState parentWindow = mWin.getParentWindow();
-        long time = System.currentTimeMillis();
-        if (time > mDeferTransactionTime + PENDING_TRANSACTION_FINISH_WAIT_TIME) {
-            mDeferTransactionTime = -1;
-            mDeferTransactionUntilFrame = -1;
-        } else if (parentWindow != null &&
-                parentWindow.mWinAnimator.hasSurface()) {
-            mSurfaceController.deferTransactionUntil(
-                    mWin.getParentWindow().mWinAnimator.mSurfaceController.getHandle(),
-                    mDeferTransactionUntilFrame);
-        }
-    }
-
     /**
      * Sometimes we need to synchronize the first frame of animation with some external event.
      * To achieve this, we prolong the start of the animation and keep producing the first frame of
@@ -2033,4 +2007,20 @@
                     DtDy * w.mVScale, false);
         }
     }
+
+    void enableSurfaceTrace(FileDescriptor fd) {
+        if (mSurfaceController != null) {
+            mSurfaceController.installRemoteTrace(fd);
+        }
+    }
+
+    void disableSurfaceTrace() {
+        if (mSurfaceController != null) {
+            try {
+                mSurfaceController.removeRemoteTrace();
+            } catch (ClassCastException e) {
+                Slog.e(TAG, "Disable surface trace for " + this + " but its not enabled");
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f5ed9d1..cc72352 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -41,6 +41,7 @@
 
 import android.util.Slog;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -70,6 +71,8 @@
     private boolean mHiddenForOtherReasons = true;
     private final String title;
 
+    private final WindowManagerService mService;
+
     public WindowSurfaceController(SurfaceSession s,
             String name, int w, int h, int format, int flags, WindowStateAnimator animator) {
         mAnimator = animator;
@@ -79,6 +82,8 @@
 
         title = name;
 
+        mService = animator.mService;
+
         // For opaque child windows placed under parent windows,
         // we use a special SurfaceControl which mirrors commands
         // to a black-out layer placed one Z-layer below the surface.
@@ -95,6 +100,19 @@
             mSurfaceControl = new SurfaceControl(
                     s, name, w, h, format, flags);
         }
+
+        if (mService.mSurfaceTraceEnabled) {
+            mSurfaceControl = new RemoteSurfaceTrace(mService.mSurfaceTraceFd.getFileDescriptor(),
+                    mSurfaceControl, animator.mWin);
+        }
+    }
+
+    void installRemoteTrace(FileDescriptor fd) {
+        mSurfaceControl = new RemoteSurfaceTrace(fd, mSurfaceControl, mAnimator.mWin);
+    }
+
+    void removeRemoteTrace() {
+        mSurfaceControl = new SurfaceControl(mSurfaceControl);
     }
 
 
@@ -127,7 +145,7 @@
     }
 
     void setPositionAndLayer(float left, float top, int layerStack, int layer) {
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             mSurfaceX = left;
             mSurfaceY = top;
@@ -146,7 +164,7 @@
                 mAnimator.reclaimSomeSurfaceMemory("create-init", true);
             }
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION setPositionAndLayer");
         }
@@ -230,11 +248,11 @@
 
     void setLayer(int layer) {
         if (mSurfaceControl != null) {
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
             try {
                 mSurfaceControl.setLayer(layer);
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
             }
         }
     }
@@ -338,11 +356,11 @@
             return;
         }
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             mSurfaceControl.setTransparentRegionHint(region);
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION setTransparentRegion");
         }
@@ -356,11 +374,11 @@
             return;
         }
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             mSurfaceControl.setOpaque(isOpaque);
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
         }
     }
@@ -373,11 +391,11 @@
             return;
         }
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             mSurfaceControl.setSecure(isSecure);
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 22f39fb..13d3501 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -37,7 +37,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.DO_TRAVERSAL;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
@@ -304,6 +303,7 @@
         mSustainedPerformanceModeCurrent = false;
         mService.mTransactionSequence++;
 
+        // TODO(multi-display): Perform same actions on all displays.
         final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
         final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
         final int defaultDw = defaultInfo.logicalWidth;
@@ -311,13 +311,13 @@
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
-        SurfaceControl.openTransaction();
+        mService.openSurfaceTransaction();
         try {
             applySurfaceChangesTransaction(recoveringMemory, numDisplays, defaultDw, defaultDh);
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
-            SurfaceControl.closeTransaction();
+            mService.closeSurfaceTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
         }
@@ -560,7 +560,7 @@
 
         // Remove all deferred displays stacks, tasks, and activities.
         for (int displayNdx = mService.mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
-            mService.mDisplayContents.valueAt(displayNdx).onCompleteDeferredRemoval();
+            mService.mDisplayContents.valueAt(displayNdx).checkCompleteDeferredRemoval();
         }
 
         if (updateInputWindowsNeeded) {
@@ -739,10 +739,6 @@
 
                 // Moved from updateWindowsAndWallpaperLocked().
                 if (w.mHasSurface) {
-                    // If we have recently synchronized a previous transaction for this
-                    // window ensure we don't push through an unsynchronized one now.
-                    winAnimator.deferToPendingTransaction();
-
                     // Take care of the window being ready to display.
                     final boolean committed = winAnimator.commitFinishDrawingLocked();
                     if (isDefaultDisplay && committed) {
@@ -869,7 +865,9 @@
             displayContent.stopDimmingIfNeeded();
 
             if (updateAllDrawn) {
-                updateAllDrawnLocked(displayContent);
+                // See if any windows have been drawn, so they (and others associated with them)
+                // can now be shown.
+                displayContent.updateAllDrawn();
             }
         }
 
@@ -917,7 +915,7 @@
         }
 
         mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
-                mService.mCurConfiguration.uiMode);
+                mService.mGlobalConfiguration.uiMode);
         if (isDefaultDisplay) {
             // Not needed on non-default displays.
             mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
@@ -1083,7 +1081,8 @@
 
         mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
 
-        mService.rebuildAppWindowListLocked();
+        final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
+        displayContent.rebuildAppWindowList();
 
         mWallpaperMayChange = false;
 
@@ -1107,7 +1106,6 @@
         }
         // Adjust wallpaper before we pull the lower/upper target, since pending changes
         // (like the clearAnimatingFlags() above) might affect wallpaper target result.
-        final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
         if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
                 mWallpaperControllerLocked.adjustWallpaperWindows()) {
             mService.mLayersController.assignLayersLocked(windows);
@@ -1158,7 +1156,7 @@
 
             voiceInteraction |= wtoken.voiceInteraction;
 
-            if (wtoken.appFullscreen) {
+            if (wtoken.fillsParent()) {
                 WindowState ws = wtoken.findMainWindow();
                 if (ws != null) {
                     animLp = ws.mAttrs;
@@ -1259,11 +1257,11 @@
 
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
-            SurfaceControl.openTransaction();
+            mService.openSurfaceTransaction();
             try {
                 mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
             } finally {
-                SurfaceControl.closeTransaction();
+                mService.closeSurfaceTransaction();
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                         "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
             }
@@ -1520,52 +1518,6 @@
         }
     }
 
-    private void updateAllDrawnLocked(DisplayContent displayContent) {
-        // See if any windows have been drawn, so they (and others
-        // associated with them) can now be shown.
-        ArrayList<TaskStack> stacks = displayContent.getStacks();
-        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                    final AppWindowToken wtoken = tokens.get(tokenNdx);
-                    if (!wtoken.allDrawn) {
-                        int numInteresting = wtoken.numInterestingWindows;
-                        if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
-                            if (DEBUG_VISIBILITY)
-                                Slog.v(TAG, "allDrawn: " + wtoken
-                                    + " interesting=" + numInteresting
-                                    + " drawn=" + wtoken.numDrawnWindows);
-                            wtoken.allDrawn = true;
-                            // Force an additional layout pass where WindowStateAnimator#
-                            // commitFinishDrawingLocked() will call performShowLocked().
-                            displayContent.layoutNeeded = true;
-                            mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN,
-                                    wtoken.token).sendToTarget();
-                        }
-                    }
-                    if (!wtoken.allDrawnExcludingSaved) {
-                        int numInteresting = wtoken.numInterestingWindowsExcludingSaved;
-                        if (numInteresting > 0
-                                && wtoken.numDrawnWindowsExcludingSaved >= numInteresting) {
-                            if (DEBUG_VISIBILITY)
-                                Slog.v(TAG, "allDrawnExcludingSaved: " + wtoken
-                                    + " interesting=" + numInteresting
-                                    + " drawn=" + wtoken.numDrawnWindowsExcludingSaved);
-                            wtoken.allDrawnExcludingSaved = true;
-                            displayContent.layoutNeeded = true;
-                            if (wtoken.isAnimatingInvisibleWithSavedSurface()
-                                    && !mService.mFinishedEarlyAnim.contains(wtoken)) {
-                                mService.mFinishedEarlyAnim.add(wtoken);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     private static int toBrightnessOverride(float value) {
         return (int)(value * PowerManager.BRIGHTNESS_ON);
     }
@@ -1573,8 +1525,7 @@
     private void processApplicationsAnimatingInPlace(int transit) {
         if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) {
             // Find the focused window
-            final WindowState win = mService.findFocusedWindowLocked(
-                    mService.getDefaultDisplayContentLocked());
+            final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow();
             if (win != null) {
                 final AppWindowToken wtoken = win.mAppToken;
                 final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
@@ -1644,8 +1595,8 @@
                 // synchronize its thumbnail surface with the surface for the
                 // open/close animation (only on the way down)
                 anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
-                        insets, thumbnailHeader, taskId, mService.mCurConfiguration.uiMode,
-                        mService.mCurConfiguration.orientation);
+                        insets, thumbnailHeader, taskId, mService.mGlobalConfiguration.uiMode,
+                        mService.mGlobalConfiguration.orientation);
                 openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
                 openingAppAnimator.deferThumbnailDestruction =
                         !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 5f9bb9a..7ed8e78 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -22,15 +22,11 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.view.IWindow;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -46,7 +42,7 @@
  * which is the handle for an Activity that it uses to display windows. For nested windows, there is
  * a WindowToken created for the parent window to manage its children.
  */
-class WindowToken extends WindowContainer {
+class WindowToken extends WindowContainer<WindowState> {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
 
     // The window manager!
@@ -92,11 +88,13 @@
 
     void removeAllWindows() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM, "removeAllWindows: removing win=" + win);
             win.removeIfPossible();
+            if (mChildren.contains(win)) {
+                removeChild(win);
+            }
         }
-        mChildren.clear();
     }
 
     void setExiting() {
@@ -110,7 +108,7 @@
         DisplayContent displayContent = null;
 
         for (int i = 0; i < count; i++) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             if (win.mWinAnimator.isAnimationSet()) {
                 delayed = true;
                 // TODO: This is technically wrong as a token can have windows on multi-displays
@@ -135,7 +133,7 @@
     int adjustAnimLayer(int adj) {
         int highestAnimLayer = -1;
         for (int j = mChildren.size() - 1; j >= 0; j--) {
-            final WindowState w = (WindowState) mChildren.get(j);
+            final WindowState w = mChildren.get(j);
             final int winHighestAnimLayer = w.adjustAnimLayer(adj);
             if (winHighestAnimLayer > highestAnimLayer) {
                 highestAnimLayer = winHighestAnimLayer;
@@ -147,7 +145,6 @@
         return highestAnimLayer;
     }
 
-    /* package level access for test. */
     WindowState getTopWindow() {
         if (mChildren.isEmpty()) {
             return null;
@@ -160,10 +157,9 @@
      * @param target The window to search for.
      * @return The index of win in windows or of the window that is an ancestor of win.
      */
-    /* package level access for test. */
     int getWindowIndex(WindowState target) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             if (w == target || w.hasChild(target)) {
                 return i;
             }
@@ -171,290 +167,34 @@
         return -1;
     }
 
-    /**
-     * Return the list of Windows in this token on the given Display.
-     * @param displayContent The display we are interested in.
-     * @return List of windows from token that are on displayContent.
-     */
-    private WindowList getTokenWindowsOnDisplay(DisplayContent displayContent) {
-        final WindowList windowList = new WindowList();
-        final WindowList displayWindows = displayContent.getWindowList();
-        final int count = displayWindows.size();
-        for (int i = 0; i < count; i++) {
-            final WindowState win = displayWindows.get(i);
-            if (win.mToken == this) {
-                windowList.add(win);
-            }
-        }
-        return windowList;
-    }
-
-    // TODO: Now that we are no longer adding child windows to token directly, the rest of the code
-    // in this method doesn't really belong here, but is it difficult to move at the moment. Need to
-    // re-evaluate when figuring-out what to do about display window list.
-    private void addChildWindow(final WindowState win) {
-        final DisplayContent displayContent = win.getDisplayContent();
-        if (displayContent == null) {
-            return;
-        }
-        final WindowState parentWindow = win.getParentWindow();
-
-        WindowList windowsOnSameDisplay = getTokenWindowsOnDisplay(displayContent);
-
-        // Figure out this window's ordering relative to the parent window.
-        final int wCount = windowsOnSameDisplay.size();
-        final int sublayer = win.mSubLayer;
-        int largestSublayer = Integer.MIN_VALUE;
-        WindowState windowWithLargestSublayer = null;
-        int i;
-        for (i = 0; i < wCount; i++) {
-            WindowState w = windowsOnSameDisplay.get(i);
-            final int wSublayer = w.mSubLayer;
-            if (wSublayer >= largestSublayer) {
-                largestSublayer = wSublayer;
-                windowWithLargestSublayer = w;
-            }
-            if (sublayer < 0) {
-                // For negative sublayers, we go below all windows in the same sublayer.
-                if (wSublayer >= sublayer) {
-                    win.addWindowToListBefore(wSublayer >= 0 ? parentWindow : w);
-                    break;
-                }
-            } else {
-                // For positive sublayers, we go above all windows in the same sublayer.
-                if (wSublayer > sublayer) {
-                    win.addWindowToListBefore(w);
-                    break;
-                }
-            }
-        }
-        if (i >= wCount) {
-            if (sublayer < 0) {
-                win.addWindowToListBefore(parentWindow);
-            } else {
-                win.addWindowToListAfter(
-                        largestSublayer >= 0 ? windowWithLargestSublayer : parentWindow);
-            }
-        }
-    }
-
     void addWindow(final WindowState win) {
         if (DEBUG_FOCUS) Slog.d(TAG_WM, "addWindow: win=" + win + " Callers=" + Debug.getCallers(5));
 
+        final DisplayContent dc = win.getDisplayContent();
         if (!win.isChildWindow()) {
             int tokenWindowsPos = 0;
-            if (asAppWindowToken() != null) {
-                tokenWindowsPos = addAppWindow(win);
-            } else {
-                win.addNonAppWindowToList();
+            if (dc != null) {
+                if (asAppWindowToken() != null) {
+                    tokenWindowsPos = dc.addAppWindowToWindowList(win);
+                } else {
+                    dc.addNonAppWindowToWindowList(win);
+                }
             }
             if (!mChildren.contains(win)) {
                 if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
-                mChildren.add(tokenWindowsPos, win);
+                addChild(win, tokenWindowsPos);
             }
-        } else {
-            addChildWindow(win);
+        } else if (dc != null) {
+            dc.addChildWindowToWindowList(win);
         }
     }
 
-    private int addAppWindow(final WindowState win) {
-        final DisplayContent displayContent = win.getDisplayContent();
-        if (displayContent == null) {
-            // It doesn't matter this display is going away.
-            return 0;
-        }
-        final IWindow client = win.mClient;
-
-        final WindowList windows = displayContent.getWindowList();
-        WindowList tokenWindowList = getTokenWindowsOnDisplay(displayContent);
-        int tokenWindowsPos = 0;
-        if (!tokenWindowList.isEmpty()) {
-            return addAppWindowExisting(win, windows, tokenWindowList);
-        }
-
-        // No windows from this token on this display
-        if (mService.localLOGV) Slog.v(TAG_WM, "Figuring out where to add app window "
-                + client.asBinder() + " (token=" + this + ")");
-
-        // Figure out where the window should go, based on the order of applications.
-        WindowState pos = null;
-
-        final ArrayList<Task> tasks = displayContent.getTasks();
-        int taskNdx;
-        int tokenNdx = -1;
-        for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-            for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                final AppWindowToken t = tokens.get(tokenNdx);
-                if (t == this) {
-                    --tokenNdx;
-                    if (tokenNdx < 0) {
-                        --taskNdx;
-                        if (taskNdx >= 0) {
-                            tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1;
-                        }
-                    }
-                    break;
-                }
-
-                // We haven't reached the token yet; if this token is not going to the bottom and
-                // has windows on this display, we can use it as an anchor for when we do reach the
-                // token.
-                tokenWindowList = getTokenWindowsOnDisplay(displayContent);
-                if (!t.sendingToBottom && tokenWindowList.size() > 0) {
-                    pos = tokenWindowList.get(0);
-                }
-            }
-            if (tokenNdx >= 0) {
-                // early exit
-                break;
-            }
-        }
-
-        // We now know the index into the apps. If we found an app window above, that gives us the
-        // position; else we need to look some more.
-        if (pos != null) {
-            // Move behind any windows attached to this one.
-            final WindowToken atoken = mService.mTokenMap.get(pos.mClient.asBinder());
-            if (atoken != null) {
-                tokenWindowList = atoken.getTokenWindowsOnDisplay(displayContent);
-                final int NC = tokenWindowList.size();
-                if (NC > 0) {
-                    WindowState bottom = tokenWindowList.get(0);
-                    if (bottom.mSubLayer < 0) {
-                        pos = bottom;
-                    }
-                }
-            }
-            win.addWindowToListBefore(pos);
-            return tokenWindowsPos;
-        }
-
-        // Continue looking down until we find the first token that has windows on this display.
-        for ( ; taskNdx >= 0; --taskNdx) {
-            AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-            for ( ; tokenNdx >= 0; --tokenNdx) {
-                final WindowToken t = tokens.get(tokenNdx);
-                tokenWindowList = t.getTokenWindowsOnDisplay(displayContent);
-                final int NW = tokenWindowList.size();
-                if (NW > 0) {
-                    pos = tokenWindowList.get(NW-1);
-                    break;
-                }
-            }
-            if (tokenNdx >= 0) {
-                // found
-                break;
-            }
-        }
-
-        if (pos != null) {
-            // Move in front of any windows attached to this one.
-            final WindowToken atoken = mService.mTokenMap.get(pos.mClient.asBinder());
-            if (atoken != null) {
-                final WindowState top = atoken.getTopWindow();
-                if (top != null && top.mSubLayer >= 0) {
-                    pos = top;
-                }
-            }
-            win.addWindowToListAfter(pos);
-            return tokenWindowsPos;
-        }
-
-        // Just search for the start of this layer.
-        final int myLayer = win.mBaseLayer;
-        int i;
-        for (i = windows.size() - 1; i >= 0; --i) {
-            WindowState w = windows.get(i);
-            // Dock divider shares the base layer with application windows, but we want to always
-            // keep it above the application windows. The sharing of the base layer is intended
-            // for window animations, which need to be above the dock divider for the duration
-            // of the animation.
-            if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) {
-                break;
-            }
-        }
-        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                "Based on layer: Adding window " + win + " at " + (i + 1) + " of "
-                        + windows.size());
-        windows.add(i + 1, win);
-        mService.mWindowsChanged = true;
-        return tokenWindowsPos;
-    }
-
-    private int addAppWindowExisting(
-            WindowState win, WindowList windowList, WindowList tokenWindowList) {
-
-        int tokenWindowsPos;
-        // If this application has existing windows, we simply place the new window on top of
-        // them... but keep the starting window on top.
-        if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
-            // Base windows go behind everything else.
-            final WindowState lowestWindow = tokenWindowList.get(0);
-            win.addWindowToListBefore(lowestWindow);
-            tokenWindowsPos = getWindowIndex(lowestWindow);
-        } else {
-            final AppWindowToken atoken = win.mAppToken;
-            final int windowListPos = tokenWindowList.size();
-            final WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
-            if (atoken != null && lastWindow == atoken.startingWindow) {
-                win.addWindowToListBefore(lastWindow);
-                tokenWindowsPos = getWindowIndex(lastWindow);
-            } else {
-                int newIdx = findIdxBasedOnAppTokens(win);
-                // There is a window above this one associated with the same apptoken note that the
-                // window could be a floating window that was created later or a window at the top
-                // of the list of windows associated with this token.
-                if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                        "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
-                                + windowList.size());
-                windowList.add(newIdx + 1, win);
-                if (newIdx < 0) {
-                    // No window from token found on win's display.
-                    tokenWindowsPos = 0;
-                } else {
-                    tokenWindowsPos = getWindowIndex(windowList.get(newIdx)) + 1;
-                }
-                mService.mWindowsChanged = true;
-            }
-        }
-        return tokenWindowsPos;
-    }
-
-    int reAddAppWindows(DisplayContent displayContent, int index) {
-        final int count = mChildren.size();
-        for (int i = 0; i < count; i++) {
-            final WindowState win = (WindowState) mChildren.get(i);
-            final DisplayContent winDisplayContent = win.getDisplayContent();
-            if (winDisplayContent == displayContent || winDisplayContent == null) {
-                win.mDisplayContent = displayContent;
-                index = win.reAddWindow(index);
-            }
-        }
-        return index;
-    }
-
-    /**
-     * This method finds out the index of a window that has the same app token as win. used for z
-     * ordering the windows in mWindows
-     */
-    private int findIdxBasedOnAppTokens(WindowState win) {
-        final WindowList windows = win.getWindowList();
-        for(int j = windows.size() - 1; j >= 0; j--) {
-            final WindowState wentry = windows.get(j);
-            if(wentry.mAppToken == win.mAppToken) {
-                return j;
-            }
-        }
-        return -1;
-    }
-
     /** Return the first window in the token window list that isn't a starting window or null. */
     WindowState getFirstNonStartingWindow() {
         final int count = mChildren.size();
         // We only care about parent windows so no need to loop through child windows.
         for (int i = 0; i < count; i++) {
-            final WindowState w = (WindowState) mChildren.get(i);
+            final WindowState w = mChildren.get(i);
             if (w.mAttrs.type != TYPE_APPLICATION_STARTING) {
                 return w;
             }
@@ -462,11 +202,6 @@
         return null;
     }
 
-    @CallSuper
-    void removeWindow(WindowState win) {
-        mChildren.remove(win);
-    }
-
     /** Returns true if the token windows list is empty. */
     boolean isEmpty() {
         return mChildren.isEmpty();
@@ -474,7 +209,7 @@
 
     WindowState getReplacingWindow() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = (WindowState) mChildren.get(i);
+            final WindowState win = mChildren.get(i);
             final WindowState replacing = win.getReplacingWindow();
             if (replacing != null) {
                 return replacing;
@@ -485,7 +220,7 @@
 
     void hideWallpaperToken(boolean wasDeferred, String reason) {
         for (int j = mChildren.size() - 1; j >= 0; j--) {
-            final WindowState wallpaper = (WindowState) mChildren.get(j);
+            final WindowState wallpaper = mChildren.get(j);
             wallpaper.hideWallpaperWindow(wasDeferred, reason);
         }
         hidden = true;
@@ -494,7 +229,7 @@
     void sendWindowWallpaperCommand(
             String action, int x, int y, int z, Bundle extras, boolean sync) {
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
+            final WindowState wallpaper = mChildren.get(wallpaperNdx);
             try {
                 wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync);
                 // We only want to be synchronous with one wallpaper.
@@ -507,7 +242,7 @@
     void updateWallpaperOffset(int dw, int dh, boolean sync) {
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
+            final WindowState wallpaper = mChildren.get(wallpaperNdx);
             if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
                 final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
                 winAnimator.computeShownFrameLocked();
@@ -528,7 +263,7 @@
 
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
+            final WindowState wallpaper = mChildren.get(wallpaperNdx);
             if (visible) {
                 wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
             }
@@ -551,7 +286,7 @@
 
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
+            final WindowState wallpaper = mChildren.get(wallpaperNdx);
 
             if (visible) {
                 wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
@@ -627,7 +362,7 @@
 
     boolean hasVisibleNotDrawnWallpaper() {
         for (int j = mChildren.size() - 1; j >= 0; --j) {
-            final WindowState wallpaper = (WindowState) mChildren.get(j);
+            final WindowState wallpaper = mChildren.get(j);
             if (wallpaper.hasVisibleNotDrawnWallpaper()) {
                 return true;
             }
@@ -638,7 +373,7 @@
     int getHighestAnimLayer() {
         int highest = -1;
         for (int j = 0; j < mChildren.size(); j++) {
-            final WindowState w = (WindowState) mChildren.get(j);
+            final WindowState w = mChildren.get(j);
             final int wLayer = w.getHighestAnimLayer();
             if (wLayer > highest) {
                 highest = wLayer;
@@ -675,4 +410,9 @@
         }
         return stringName;
     }
+
+    @Override
+    String getName() {
+        return toString();
+    }
 }
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 3cc9ec6..9459517 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -38,8 +38,6 @@
     frameworks/base/libs/hwui \
     frameworks/base/core/jni \
     frameworks/native/services \
-    libcore/include \
-    libcore/include/libsuspend \
     system/security/keystore/include \
     $(call include-path-for, libhardware)/hardware \
     $(call include-path-for, libhardware_legacy)/hardware_legacy \
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f9129ee..af84c2a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -309,6 +309,12 @@
 
     private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
 
+    /**
+     * Minimum timeout in milliseconds after which unlocking with weak auth times out,
+     * i.e. the user has to use a strong authentication method like password, PIN or pattern.
+     */
+    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -525,6 +531,7 @@
 
     static class ActiveAdmin {
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
+        private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
         private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
         private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
@@ -549,6 +556,7 @@
         private static final String TAG_PERMITTED_IMES = "permitted-imes";
         private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max-failed-password-wipe";
         private static final String TAG_MAX_TIME_TO_UNLOCK = "max-time-to-unlock";
+        private static final String TAG_STRONG_AUTH_UNLOCK_TIMEOUT = "strong-auth-unlock-timeout";
         private static final String TAG_MIN_PASSWORD_NONLETTER = "min-password-nonletter";
         private static final String TAG_MIN_PASSWORD_SYMBOLS = "min-password-symbols";
         private static final String TAG_MIN_PASSWORD_NUMERIC = "min-password-numeric";
@@ -603,6 +611,8 @@
         static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
         long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
 
+        long strongAuthUnlockTimeout = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+
         static final int DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE = 0;
         int maximumFailedPasswordsForWipe = DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE;
 
@@ -617,6 +627,7 @@
         int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
 
         boolean encryptionRequested = false;
+        boolean testOnlyAdmin = false;
         boolean disableCamera = false;
         boolean disableCallerId = false;
         boolean disableContactsSearch = false;
@@ -751,6 +762,11 @@
                 out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock));
                 out.endTag(null, TAG_MAX_TIME_TO_UNLOCK);
             }
+            if (strongAuthUnlockTimeout != DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
+                out.startTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
+                out.attribute(null, ATTR_VALUE, Long.toString(strongAuthUnlockTimeout));
+                out.endTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
+            }
             if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
                 out.startTag(null, TAG_MAX_FAILED_PASSWORD_WIPE);
                 out.attribute(null, ATTR_VALUE, Integer.toString(maximumFailedPasswordsForWipe));
@@ -786,6 +802,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(encryptionRequested));
                 out.endTag(null, TAG_ENCRYPTION_REQUESTED);
             }
+            if (testOnlyAdmin) {
+                out.startTag(null, TAG_TEST_ONLY_ADMIN);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(testOnlyAdmin));
+                out.endTag(null, TAG_TEST_ONLY_ADMIN);
+            }
             if (disableCamera) {
                 out.startTag(null, TAG_DISABLE_CAMERA);
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
@@ -960,6 +981,9 @@
                 } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
                     maximumTimeToUnlock = Long.parseLong(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
+                    strongAuthUnlockTimeout = Long.parseLong(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) {
                     maximumFailedPasswordsForWipe = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -981,6 +1005,9 @@
                 } else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) {
                     encryptionRequested = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) {
+                    testOnlyAdmin = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_CAMERA.equals(tag)) {
                     disableCamera = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -1179,6 +1206,8 @@
 
         void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("uid="); pw.println(getUid());
+            pw.print(prefix); pw.print("testOnlyAdmin=");
+            pw.println(testOnlyAdmin);
             pw.print(prefix); pw.println("policies:");
             ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
             if (pols != null) {
@@ -1206,6 +1235,8 @@
                     pw.println(minimumPasswordNonLetter);
             pw.print(prefix); pw.print("maximumTimeToUnlock=");
                     pw.println(maximumTimeToUnlock);
+            pw.print(prefix); pw.print("strongAuthUnlockTimeout=");
+                    pw.println(strongAuthUnlockTimeout);
             pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
                     pw.println(maximumFailedPasswordsForWipe);
             pw.print(prefix); pw.print("specifiesGlobalProxy=");
@@ -2829,8 +2860,9 @@
         synchronized (this) {
             long ident = mInjector.binderClearCallingIdentity();
             try {
-                if (!refreshing
-                        && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
+                final ActiveAdmin existingAdmin
+                        = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+                if (!refreshing && existingAdmin != null) {
                     throw new IllegalArgumentException("Admin is already added");
                 }
                 if (policy.mRemovingAdmins.contains(adminReceiver)) {
@@ -2838,6 +2870,9 @@
                             "Trying to set an admin which is being removed");
                 }
                 ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
+                newAdmin.testOnlyAdmin =
+                        (existingAdmin != null) ? existingAdmin.testOnlyAdmin
+                                : isPackageTestOnly(adminReceiver.getPackageName(), userHandle);
                 policy.mAdminMap.put(adminReceiver, newAdmin);
                 int replaceIndex = -1;
                 final int N = policy.mAdminList.size();
@@ -2949,12 +2984,13 @@
         enforceShell("forceRemoveActiveAdmin");
         long ident = mInjector.binderClearCallingIdentity();
         try {
-            if (!isPackageTestOnly(adminReceiver.getPackageName(), userHandle)) {
-                throw new SecurityException("Attempt to remove non-test admin "
-                        + adminReceiver + " " + userHandle);
-            }
-            // If admin is a device or profile owner tidy that up first.
             synchronized (this)  {
+                if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
+                    throw new SecurityException("Attempt to remove non-test admin "
+                            + adminReceiver + " " + userHandle);
+                }
+
+                // If admin is a device or profile owner tidy that up first.
                 if (isDeviceOwner(adminReceiver, userHandle)) {
                     clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
                 }
@@ -2972,6 +3008,17 @@
         }
     }
 
+    /**
+     * Return if a given package has testOnly="true", in which case we'll relax certain rules
+     * for CTS.
+     *
+     * DO NOT use this method except in {@link #setActiveAdmin}.  Use {@link #isAdminTestOnlyLocked}
+     * to check wehter an active admin is test-only or not.
+     *
+     * The system allows this flag to be changed when an app is updated, which is not good
+     * for us.  So we persist the flag in {@link ActiveAdmin} when an admin is first installed,
+     * and used the persisted version in actual checks. (See b/31382361 and b/28928996)
+     */
     private boolean isPackageTestOnly(String packageName, int userHandle) {
         final ApplicationInfo ai;
         try {
@@ -2988,6 +3035,14 @@
         return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
     }
 
+    /**
+     * See {@link #isPackageTestOnly}.
+     */
+    private boolean isAdminTestOnlyLocked(ComponentName who, int userHandle) {
+        final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+        return (admin != null) && admin.testOnlyAdmin;
+    }
+
     private void enforceShell(String method) {
         final int callingUid = Binder.getCallingUid();
         if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
@@ -4187,6 +4242,60 @@
     }
 
     @Override
+    public void setRequiredStrongAuthTimeout(ComponentName who, long timeoutMs,
+            boolean parent) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkArgument(timeoutMs >= MINIMUM_STRONG_AUTH_TIMEOUT_MS,
+                "Timeout must not be lower than the minimum strong auth timeout.");
+        Preconditions.checkArgument(timeoutMs <= DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS,
+                "Timeout must not be higher than the default strong auth timeout.");
+
+        final int userHandle = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            if (ap.strongAuthUnlockTimeout != timeoutMs) {
+                ap.strongAuthUnlockTimeout = timeoutMs;
+                saveSettingsLocked(userHandle);
+            }
+        }
+    }
+
+    /**
+     * Return a single admin's strong auth unlock timeout or minimum value (strictest) of all
+     * admins if who is null.
+     * Returns default timeout if not configured.
+     */
+    @Override
+    public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
+        if (!mHasFeature) {
+            return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+        }
+        enforceFullCrossUsersPermission(userId);
+        synchronized (this) {
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId, parent);
+                return admin != null ? Math.max(admin.strongAuthUnlockTimeout,
+                        MINIMUM_STRONG_AUTH_TIMEOUT_MS)
+                        : DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+            }
+
+            // Return the strictest policy across all participating admins.
+            List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId, parent);
+
+            long strongAuthUnlockTimeout = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+            for (int i = 0; i < admins.size(); i++) {
+                strongAuthUnlockTimeout = Math.min(admins.get(i).strongAuthUnlockTimeout,
+                        strongAuthUnlockTimeout);
+            }
+            return Math.max(strongAuthUnlockTimeout, MINIMUM_STRONG_AUTH_TIMEOUT_MS);
+        }
+    }
+
+    @Override
     public void lockNow(boolean parent) {
         if (!mHasFeature) {
             return;
@@ -6251,7 +6360,7 @@
      * The profile owner can only be set before the user setup phase has completed,
      * except for:
      * - SYSTEM_UID
-     * - adb if there are no accounts. (But see {@link #hasIncompatibleAccounts})
+     * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked})
      */
     private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) {
         UserInfo info = getUserInfo(userHandle);
@@ -6274,7 +6383,7 @@
         int callingUid = mInjector.binderGetCallingUid();
         if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             if (hasUserSetupCompleted(userHandle)
-                    && hasIncompatibleAccounts(userHandle, owner)) {
+                    && hasIncompatibleAccountsLocked(userHandle, owner)) {
                 throw new IllegalStateException("Not allowed to set the profile owner because "
                         + "there are already some accounts on the profile");
             }
@@ -6298,7 +6407,7 @@
             enforceCanManageProfileAndDeviceOwners();
         }
 
-        final int code = checkSetDeviceOwnerPreCondition(owner, userId, isAdb);
+        final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb);
         switch (code) {
             case CODE_OK:
                 return;
@@ -8566,7 +8675,7 @@
      * The device owner can only be set before the setup phase of the primary user has completed,
      * except for adb command if no accounts or additional users are present on the device.
      */
-    private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition(
+    private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreConditionLocked(
             @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb) {
         if (mOwners.hasDeviceOwner()) {
             return CODE_HAS_DEVICE_OWNER;
@@ -8584,7 +8693,7 @@
                     if (mUserManager.getUserCount() > 1) {
                         return CODE_NONSYSTEM_USER_EXISTS;
                     }
-                    if (hasIncompatibleAccounts(UserHandle.USER_SYSTEM, owner)) {
+                    if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
                         return CODE_ACCOUNTS_NOT_EMPTY;
                     }
                 } else {
@@ -8610,8 +8719,10 @@
     }
 
     private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
-        return CODE_OK == checkSetDeviceOwnerPreCondition(
-                /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false);
+        synchronized (this) {
+            return CODE_OK == checkSetDeviceOwnerPreConditionLocked(
+                    /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false);
+        }
     }
 
     private boolean hasFeatureManagedUsers() {
@@ -9257,7 +9368,7 @@
      *   ..._DISALLOWED, return true.
      * - Otherwise return false.
      */
-    private boolean hasIncompatibleAccounts(int userId, @Nullable ComponentName owner) {
+    private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) {
         final long token = mInjector.binderClearCallingIdentity();
         try {
             final AccountManager am = AccountManager.get(mContext);
@@ -9295,7 +9406,7 @@
                 // Owner is unknown.  Suppose it's not test-only
                 compatible = false;
                 log = "Only test-only device/profile owner can be installed with accounts";
-            } else if (isPackageTestOnly(owner.getPackageName(), userId)) {
+            } else if (isAdminTestOnlyLocked(owner, userId)) {
                 if (compatible) {
                     log = "Installing test-only owner " + owner;
                 } else {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fb4ea30..96e9cb4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -596,6 +596,10 @@
         boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices", false);
         boolean disableSamplingProfiler = SystemProperties.getBoolean("config.disable_samplingprof",
                 false);
+
+        boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false);
+        boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
+
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
 
         try {
@@ -651,10 +655,12 @@
             ServiceManager.addService("vibrator", vibrator);
             traceEnd();
 
-            traceBeginAndSlog("StartConsumerIrService");
-            consumerIr = new ConsumerIrService(context);
-            ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
-            traceEnd();
+            if (!disableConsumerIr) {
+                traceBeginAndSlog("StartConsumerIrService");
+                consumerIr = new ConsumerIrService(context);
+                ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
+                traceEnd();
+            }
 
             traceBeginAndSlog("StartAlarmManagerService");
             mSystemServiceManager.startService(AlarmManagerService.class);
@@ -677,9 +683,11 @@
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
             traceEnd();
 
-            traceBeginAndSlog("StartVrManagerService");
-            mSystemServiceManager.startService(VrManagerService.class);
-            traceEnd();
+            if (!disableVrManager) {
+                traceBeginAndSlog("StartVrManagerService");
+                mSystemServiceManager.startService(VrManagerService.class);
+                traceEnd();
+            }
 
             mActivityManagerService.setWindowManager(wm);
 
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 4bb0902..4c75452 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -19,6 +19,7 @@
 import static android.system.OsConstants.*;
 
 import android.os.SystemClock;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.net.apf.ApfGenerator;
@@ -44,6 +45,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.Thread;
+import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
@@ -171,8 +173,8 @@
     private static final int ETH_HEADER_LEN = 14;
     private static final int ETH_DEST_ADDR_OFFSET = 0;
     private static final int ETH_ETHERTYPE_OFFSET = 12;
-    private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
-            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+    private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
+            {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
     // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
     private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
     // Endianness is not an issue for this constant because the APF interpreter always operates in
@@ -181,6 +183,7 @@
     private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
     private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
     private static final int IPV4_ANY_HOST_ADDRESS = 0;
+    private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
 
     private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
     private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
@@ -188,7 +191,7 @@
     private static final int IPV6_HEADER_LEN = 40;
     // The IPv6 all nodes address ff02::1
     private static final byte[] IPV6_ALL_NODES_ADDRESS =
-            new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+            { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
     private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
@@ -206,7 +209,7 @@
     private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
     private static final short ARP_OPCODE_REQUEST = 1;
     private static final short ARP_OPCODE_REPLY = 2;
-    private static final byte[] ARP_IPV4_HEADER = new byte[]{
+    private static final byte[] ARP_IPV4_HEADER = {
             0, 1, // Hardware type: Ethernet (1)
             8, 0, // Protocol type: IP (0x0800)
             6,    // Hardware size: 6
@@ -229,6 +232,9 @@
     // Our IPv4 address, if we have just one, otherwise null.
     @GuardedBy("this")
     private byte[] mIPv4Address;
+    // The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null.
+    @GuardedBy("this")
+    private int mIPv4PrefixLength;
 
     @VisibleForTesting
     ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
@@ -364,26 +370,6 @@
 
         // Can't be static because it's in a non-static inner class.
         // TODO: Make this static once RA is its own class.
-        private int uint8(byte b) {
-            return b & 0xff;
-        }
-
-        private int uint16(short s) {
-            return s & 0xffff;
-        }
-
-        private long uint32(int i) {
-            return i & 0xffffffffL;
-        }
-
-        private long getUint16(ByteBuffer buffer, int position) {
-            return uint16(buffer.getShort(position));
-        }
-
-        private long getUint32(ByteBuffer buffer, int position) {
-            return uint32(buffer.getInt(position));
-        }
-
         private void prefixOptionToString(StringBuffer sb, int offset) {
             String prefix = IPv6AddresstoString(offset + 16);
             int length = uint8(mPacket.get(offset + 2));
@@ -737,39 +723,57 @@
         // Here's a basic summary of what the IPv4 filter program does:
         //
         // if filtering multicast (i.e. multicast lock not held):
-        //   if it's multicast:
-        //     drop
-        //   if it's not broadcast:
+        //   if it's DHCP destined to our MAC:
         //     pass
-        //   if it's not DHCP destined to our MAC:
+        //   if it's L2 broadcast:
+        //     drop
+        //   if it's IPv4 multicast:
+        //     drop
+        //   if it's IPv4 broadcast:
         //     drop
         // pass
 
         if (mMulticastFilter) {
-            // Check for multicast destination address range
+            final String skipDhcpv4Filter = "skip_dhcp_v4_filter";
+
+            // Pass DHCP addressed to us.
+            // Check it's UDP.
+            gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+            gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter);
+            // Check it's not a fragment. This matches the BPF filter installed by the DHCP client.
+            gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
+            gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter);
+            // Check it's addressed to DHCP client port.
+            gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+            gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
+            gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter);
+            // Check it's DHCP to our MAC address.
+            gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
+            // NOTE: Relies on R1 containing IPv4 header offset.
+            gen.addAddR1();
+            gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
+            gen.addJump(gen.PASS_LABEL);
+
+            // Drop all multicasts/broadcasts.
+            gen.defineLabel(skipDhcpv4Filter);
+
+            // If IPv4 destination address is in multicast range, drop.
             gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
             gen.addAnd(0xf0);
             gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
 
-            // Drop all broadcasts besides DHCP addressed to us
-            // If not a broadcast packet, pass
+            // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
+            gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
+            gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, gen.DROP_LABEL);
+            if (mIPv4Address != null && mIPv4PrefixLength < 31) {
+                int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
+                gen.addJumpIfR0Equals(broadcastAddr, gen.DROP_LABEL);
+            }
+
+            // If L2 broadcast packet, drop.
             gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
             gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
-            // If not UDP, drop
-            gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
-            gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
-            // If fragment, drop. This matches the BPF filter installed by the DHCP client.
-            gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
-            gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
-            // If not to DHCP client port, drop
-            gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
-            gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
-            gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
-            // If not DHCP to our MAC address, drop
-            gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
-            // NOTE: Relies on R1 containing IPv4 header offset.
-            gen.addAddR1();
-            gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+            gen.addJump(gen.DROP_LABEL);
         }
 
         // Otherwise, pass
@@ -1062,26 +1066,32 @@
         }
     }
 
-    // Find the single IPv4 address if there is one, otherwise return null.
-    private static byte[] findIPv4Address(LinkProperties lp) {
-        byte[] ipv4Address = null;
-        for (InetAddress inetAddr : lp.getAddresses()) {
-            byte[] addr = inetAddr.getAddress();
-            if (addr.length != 4) continue;
-            // More than one IPv4 address, abort
-            if (ipv4Address != null && !Arrays.equals(ipv4Address, addr)) return null;
-            ipv4Address = addr;
+    /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
+    private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
+        LinkAddress ipv4Address = null;
+        for (LinkAddress address : lp.getLinkAddresses()) {
+            if (!(address.getAddress() instanceof Inet4Address)) {
+                continue;
+            }
+            if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) {
+                // More than one IPv4 address, abort.
+                return null;
+            }
+            ipv4Address = address;
         }
         return ipv4Address;
     }
 
     public synchronized void setLinkProperties(LinkProperties lp) {
         // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
-        byte[] ipv4Address = findIPv4Address(lp);
-        // If ipv4Address is the same as mIPv4Address, then there's no change, just return.
-        if (Arrays.equals(ipv4Address, mIPv4Address)) return;
-        // Otherwise update mIPv4Address and install new program.
-        mIPv4Address = ipv4Address;
+        final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
+        final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
+        final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
+        if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
+            return;
+        }
+        mIPv4Address = addr;
+        mIPv4PrefixLength = prefix;
         installNewProgramLocked();
     }
 
@@ -1127,4 +1137,38 @@
             pw.decreaseIndent();
         }
     }
+
+    private static int uint8(byte b) {
+        return b & 0xff;
+    }
+
+    private static int uint16(short s) {
+        return s & 0xffff;
+    }
+
+    private static long uint32(int i) {
+        return i & 0xffffffffL;
+    }
+
+    private static long getUint16(ByteBuffer buffer, int position) {
+        return uint16(buffer.getShort(position));
+    }
+
+    private static long getUint32(ByteBuffer buffer, int position) {
+        return uint32(buffer.getInt(position));
+    }
+
+    // TODO: move to android.net.NetworkUtils
+    @VisibleForTesting
+    public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
+        return bytesToInt(addrBytes) | (int) (uint32(-1) >>> prefixLength);
+    }
+
+    @VisibleForTesting
+    public static int bytesToInt(byte[] addrBytes) {
+        return (uint8(addrBytes[0]) << 24)
+                + (uint8(addrBytes[1]) << 16)
+                + (uint8(addrBytes[2]) << 8)
+                + (uint8(addrBytes[3]));
+    }
 }
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 811c0e6..97d8bdd 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -22,6 +22,7 @@
     guava \
     android-support-test \
     mockito-target-minus-junit4 \
+    platform-test-annotations \
     ShortcutManagerTestUtils
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index bd76118..f7c61d1 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -20,6 +20,9 @@
 
 import com.android.frameworks.servicestests.R;
 
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
 import android.net.apf.ApfCapabilities;
 import android.net.apf.ApfFilter;
 import android.net.apf.ApfGenerator;
@@ -28,8 +31,6 @@
 import android.net.ip.IpManager;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
 import android.os.ConditionVariable;
 import android.os.Parcelable;
 import android.system.ErrnoException;
@@ -61,7 +62,7 @@
  * Tests for APF program generator and interpreter.
  *
  * Build, install and run with:
- *  runtest frameworks-services -c com.android.server.ApfTest
+ *  runtest frameworks-services -c android.net.apf.ApfTest
  */
 public class ApfTest extends AndroidTestCase {
     private static final int TIMEOUT_MS = 500;
@@ -86,21 +87,45 @@
     private final static boolean DROP_MULTICAST = true;
     private final static boolean ALLOW_MULTICAST = false;
 
+    private static String label(int code) {
+        switch (code) {
+            case PASS: return "PASS";
+            case DROP: return "DROP";
+            default:   return "UNKNOWN";
+        }
+    }
+
+    private static void assertReturnCodesEqual(int expected, int got) {
+        assertEquals(label(expected), label(got));
+    }
+
     private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
-        assertEquals(expected, apfSimulate(program, packet, filterAge));
+        assertReturnCodesEqual(expected, apfSimulate(program, packet, filterAge));
+    }
+
+    private void assertVerdict(int expected, byte[] program, byte[] packet) {
+        assertReturnCodesEqual(expected, apfSimulate(program, packet, 0));
     }
 
     private void assertPass(byte[] program, byte[] packet, int filterAge) {
         assertVerdict(PASS, program, packet, filterAge);
     }
 
+    private void assertPass(byte[] program, byte[] packet) {
+        assertVerdict(PASS, program, packet);
+    }
+
     private void assertDrop(byte[] program, byte[] packet, int filterAge) {
         assertVerdict(DROP, program, packet, filterAge);
     }
 
+    private void assertDrop(byte[] program, byte[] packet) {
+        assertVerdict(DROP, program, packet);
+    }
+
     private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
             throws IllegalInstructionException {
-        assertEquals(expected, apfSimulate(gen.generate(), packet, filterAge));
+        assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, filterAge));
     }
 
     private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
@@ -516,7 +541,7 @@
         gen = new ApfGenerator();
         gen.addLoadImmediate(Register.R0, 1);
         gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
-        byte[] packet123 = new byte[]{0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
+        byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
         assertPass(gen, packet123, 0);
         gen = new ApfGenerator();
         gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
@@ -524,7 +549,7 @@
         gen = new ApfGenerator();
         gen.addLoadImmediate(Register.R0, 1);
         gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
-        byte[] packet12345 = new byte[]{0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
+        byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
         assertDrop(gen, packet12345, 0);
         gen = new ApfGenerator();
         gen.addLoadImmediate(Register.R0, 1);
@@ -575,12 +600,12 @@
     }
 
     private static class TestApfFilter extends ApfFilter {
-        public final static byte[] MOCK_MAC_ADDR = new byte[]{1,2,3,4,5,6};
+        public final static byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
         private FileDescriptor mWriteSocket;
 
         public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
                 IpConnectivityLog log) throws Exception {
-            super(new ApfCapabilities(2, 1536, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
+            super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
                     ipManagerCallback, multicastFilter, log);
         }
 
@@ -620,19 +645,21 @@
     private static final int ETH_HEADER_LEN = 14;
     private static final int ETH_DEST_ADDR_OFFSET = 0;
     private static final int ETH_ETHERTYPE_OFFSET = 12;
-    private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
-        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+    private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
+            {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
 
     private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
     private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
     private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+    private static final byte[] IPV4_BROADCAST_ADDRESS =
+            {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
 
     private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
     private static final int IPV6_HEADER_LEN = 40;
     private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
     // The IPv6 all nodes address ff02::1
     private static final byte[] IPV6_ALL_NODES_ADDRESS =
-            new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+            { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
     private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
@@ -670,14 +697,14 @@
     private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
 
     private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
-    private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
+    private static final byte[] ARP_IPV4_REQUEST_HEADER = {
             0, 1, // Hardware type: Ethernet (1)
             8, 0, // Protocol type: IP (0x0800)
             6,    // Hardware size: 6
             4,    // Protocol size: 4
             0, 1  // Opcode: request (1)
     };
-    private static final byte[] ARP_IPV4_REPLY_HEADER = new byte[]{
+    private static final byte[] ARP_IPV4_REPLY_HEADER = {
             0, 1, // Hardware type: Ethernet (1)
             8, 0, // Protocol type: IP (0x0800)
             6,    // Hardware size: 6
@@ -686,38 +713,63 @@
     };
     private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
 
-    private static final byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
-    private static final byte[] ANOTHER_IPV4_ADDR = new byte[]{10, 0, 0, 2};
-    private static final byte[] IPV4_ANY_HOST_ADDR = new byte[]{0, 0, 0, 0};
+    private static final byte[] MOCK_IPV4_ADDR           = {10, 0, 0, 1};
+    private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19
+    private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
+    private static final byte[] ANOTHER_IPV4_ADDR        = {10, 0, 0, 2};
+    private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
     @LargeTest
     public void testApfFilterIPv4() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
+        LinkProperties lp = new LinkProperties();
+        lp.addLinkAddress(link);
+
         ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
+        apfFilter.setLinkProperties(lp);
+
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify empty packet of 100 zero bytes is passed
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
-        assertPass(program, packet.array(), 0);
+        assertPass(program, packet.array());
 
         // Verify unicast IPv4 packet is passed
+        put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
-        assertPass(program, packet.array(), 0);
+        put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_IPV4_ADDR);
+        assertPass(program, packet.array());
 
-        // Verify broadcast IPv4, not DHCP to us, is dropped
-        packet.put(ETH_BROADCAST_MAC_ADDRESS);
-        assertDrop(program, packet.array(), 0);
+        // Verify L2 unicast to IPv4 broadcast addresses is dropped (b/30231088)
+        put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
+        assertDrop(program, packet.array());
+        put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
+        assertDrop(program, packet.array());
+
+        // Verify multicast/broadcast IPv4, not DHCP to us, is dropped
+        put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+        assertDrop(program, packet.array());
         packet.put(IPV4_VERSION_IHL_OFFSET, (byte)0x45);
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
         packet.put(IPV4_PROTOCOL_OFFSET, (byte)IPPROTO_UDP);
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
         packet.putShort(UDP_DESTINATION_PORT_OFFSET, (short)DHCP_CLIENT_PORT);
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
+        put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_MULTICAST_IPV4_ADDR);
+        assertDrop(program, packet.array());
+        put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
+        assertDrop(program, packet.array());
+        put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
+        assertDrop(program, packet.array());
 
         // Verify broadcast IPv4 DHCP to us is passed
-        packet.position(DHCP_CLIENT_MAC_OFFSET);
-        packet.put(TestApfFilter.MOCK_MAC_ADDR);
-        assertPass(program, packet.array(), 0);
+        put(packet, DHCP_CLIENT_MAC_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
+        assertPass(program, packet.array());
+
+        // Verify unicast IPv4 DHCP to us is passed
+        put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
+        assertPass(program, packet.array());
 
         apfFilter.shutdown();
     }
@@ -731,82 +783,108 @@
         // Verify empty IPv6 packet is passed
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
-        assertPass(program, packet.array(), 0);
+        assertPass(program, packet.array());
 
         // Verify empty ICMPv6 packet is passed
         packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
-        assertPass(program, packet.array(), 0);
+        assertPass(program, packet.array());
 
         // Verify empty ICMPv6 NA packet is passed
         packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_NEIGHBOR_ANNOUNCEMENT);
-        assertPass(program, packet.array(), 0);
+        assertPass(program, packet.array());
 
         // Verify ICMPv6 NA to ff02::1 is dropped
-        packet.position(IPV6_DEST_ADDR_OFFSET);
-        packet.put(IPV6_ALL_NODES_ADDRESS);
-        assertDrop(program, packet.array(), 0);
+        put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS);
+        assertDrop(program, packet.array());
 
         apfFilter.shutdown();
     }
 
     @LargeTest
     public void testApfFilterMulticast() throws Exception {
+        final byte[] unicastIpv4Addr   = {(byte)192,0,2,63};
+        final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
+        final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
+        final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
+
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
+        LinkProperties lp = new LinkProperties();
+        lp.addLinkAddress(link);
+
         ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
+        apfFilter.setLinkProperties(lp);
+
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Construct IPv4 and IPv6 multicast packets.
         ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
         mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
-        mcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
-        mcastv4packet.put(new byte[]{(byte)224,0,0,1});
+        put(mcastv4packet, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
 
         ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
         mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
         mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
-        mcastv6packet.position(IPV6_DEST_ADDR_OFFSET);
-        mcastv6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+        put(mcastv6packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
 
         // Construct IPv4 broadcast packet.
-        ByteBuffer bcastv4packet = ByteBuffer.wrap(new byte[100]);
-        bcastv4packet.put(ETH_BROADCAST_MAC_ADDRESS);
-        bcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
-        bcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
-        bcastv4packet.put(new byte[]{(byte)192,(byte)0,(byte)2,(byte)63});
+        ByteBuffer bcastv4packet1 = ByteBuffer.wrap(new byte[100]);
+        bcastv4packet1.put(ETH_BROADCAST_MAC_ADDRESS);
+        bcastv4packet1.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        put(bcastv4packet1, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
+
+        ByteBuffer bcastv4packet2 = ByteBuffer.wrap(new byte[100]);
+        bcastv4packet2.put(ETH_BROADCAST_MAC_ADDRESS);
+        bcastv4packet2.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        put(bcastv4packet2, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
+
+        // Construct IPv4 broadcast with L2 unicast address packet (b/30231088).
+        ByteBuffer bcastv4unicastl2packet = ByteBuffer.wrap(new byte[100]);
+        bcastv4unicastl2packet.put(TestApfFilter.MOCK_MAC_ADDR);
+        bcastv4unicastl2packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        put(bcastv4unicastl2packet, IPV4_DEST_ADDR_OFFSET, broadcastIpv4Addr);
 
         // Verify initially disabled multicast filter is off
-        assertPass(program, bcastv4packet.array(), 0);
-        assertPass(program, mcastv4packet.array(), 0);
-        assertPass(program, mcastv6packet.array(), 0);
+        assertPass(program, mcastv4packet.array());
+        assertPass(program, mcastv6packet.array());
+        assertPass(program, bcastv4packet1.array());
+        assertPass(program, bcastv4packet2.array());
+        assertPass(program, bcastv4unicastl2packet.array());
 
         // Turn on multicast filter and verify it works
         ipManagerCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(true);
         program = ipManagerCallback.getApfProgram();
-        assertDrop(program, bcastv4packet.array(), 0);
-        assertDrop(program, mcastv4packet.array(), 0);
-        assertDrop(program, mcastv6packet.array(), 0);
+        assertDrop(program, mcastv4packet.array());
+        assertDrop(program, mcastv6packet.array());
+        assertDrop(program, bcastv4packet1.array());
+        assertDrop(program, bcastv4packet2.array());
+        assertDrop(program, bcastv4unicastl2packet.array());
 
         // Turn off multicast filter and verify it's off
         ipManagerCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(false);
         program = ipManagerCallback.getApfProgram();
-        assertPass(program, bcastv4packet.array(), 0);
-        assertPass(program, mcastv4packet.array(), 0);
-        assertPass(program, mcastv6packet.array(), 0);
+        assertPass(program, mcastv4packet.array());
+        assertPass(program, mcastv6packet.array());
+        assertPass(program, bcastv4packet1.array());
+        assertPass(program, bcastv4packet2.array());
+        assertPass(program, bcastv4unicastl2packet.array());
 
         // Verify it can be initialized to on
         ipManagerCallback.resetApfProgramWait();
         apfFilter.shutdown();
         apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
+        apfFilter.setLinkProperties(lp);
         program = ipManagerCallback.getApfProgram();
-        assertDrop(program, bcastv4packet.array(), 0);
-        assertDrop(program, mcastv4packet.array(), 0);
-        assertDrop(program, mcastv6packet.array(), 0);
+        assertDrop(program, mcastv4packet.array());
+        assertDrop(program, mcastv6packet.array());
+        assertDrop(program, bcastv4packet1.array());
+        assertDrop(program, bcastv4unicastl2packet.array());
 
         // Verify that ICMPv6 multicast is not dropped.
         mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
-        assertPass(program, mcastv6packet.array(), 0);
+        assertPass(program, mcastv6packet.array());
 
         apfFilter.shutdown();
     }
@@ -819,17 +897,17 @@
 
     private void verifyArpFilter(byte[] program, int filterResult) {
         // Verify ARP request packet
-        assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR), 0);
-        assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR), 0);
-        assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR), 0);
+        assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR));
+        assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR));
+        assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR));
 
         // Verify unicast ARP reply packet is always accepted.
-        assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR), 0);
-        assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR), 0);
-        assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR), 0);
+        assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR));
+        assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR));
+        assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR));
 
         // Verify GARP reply packets are always filtered
-        assertDrop(program, garpReply(), 0);
+        assertDrop(program, garpReply());
     }
 
     @LargeTest
@@ -855,34 +933,26 @@
     private static byte[] arpRequestBroadcast(byte[] tip) {
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
-        packet.position(ETH_DEST_ADDR_OFFSET);
-        packet.put(ETH_BROADCAST_MAC_ADDRESS);
-        packet.position(ARP_HEADER_OFFSET);
-        packet.put(ARP_IPV4_REQUEST_HEADER);
-        packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
-        packet.put(tip);
+        put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+        put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+        put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
         return packet.array();
     }
 
     private static byte[] arpReplyUnicast(byte[] tip) {
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
-        packet.position(ARP_HEADER_OFFSET);
-        packet.put(ARP_IPV4_REPLY_HEADER);
-        packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
-        packet.put(tip);
+        put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+        put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
         return packet.array();
     }
 
     private static byte[] garpReply() {
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
-        packet.position(ETH_DEST_ADDR_OFFSET);
-        packet.put(ETH_BROADCAST_MAC_ADDRESS);
-        packet.position(ARP_HEADER_OFFSET);
-        packet.put(ARP_IPV4_REPLY_HEADER);
-        packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
-        packet.put(IPV4_ANY_HOST_ADDR);
+        put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+        put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+        put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, IPV4_ANY_HOST_ADDR);
         return packet.array();
     }
 
@@ -893,22 +963,22 @@
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify new program should drop RA for 1/6th its lifetime
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
         assertDrop(program, packet.array(), lifetime/6);
         assertPass(program, packet.array(), lifetime/6 + 1);
         assertPass(program, packet.array(), lifetime);
 
         // Verify RA checksum is ignored
         packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
         packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
 
         // Verify other changes to RA make it not match filter
         packet.put(0, (byte)-1);
-        assertPass(program, packet.array(), 0);
+        assertPass(program, packet.array());
         packet.put(0, (byte)0);
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, packet.array());
     }
 
     // Test that when ApfFilter is shown the given packet, it generates a program to filter it
@@ -973,7 +1043,7 @@
         basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
         basePacket.position(IPV6_DEST_ADDR_OFFSET);
         basePacket.put(IPV6_ALL_NODES_ADDRESS);
-        assertPass(program, basePacket.array(), 0);
+        assertPass(program, basePacket.array());
 
         testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
         verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1));
@@ -1069,6 +1139,13 @@
         return file.getAbsolutePath();
     }
 
+    private static void put(ByteBuffer buffer, int position, byte[] bytes) {
+        final int original = buffer.position();
+        buffer.position(position);
+        buffer.put(bytes);
+        buffer.position(original);
+    }
+
     /**
      * Call the APF interpreter the run {@code program} on {@code packet} pretending the
      * filter was installed {@code filter_age} seconds ago.
@@ -1089,4 +1166,30 @@
      */
     private native static boolean compareBpfApf(String filter, String pcap_filename,
             byte[] apf_program);
+
+    public void testBytesToInt() {
+        assertEquals(0x00000000, ApfFilter.bytesToInt(IPV4_ANY_HOST_ADDR));
+        assertEquals(0xffffffff, ApfFilter.bytesToInt(IPV4_BROADCAST_ADDRESS));
+        assertEquals(0x0a000001, ApfFilter.bytesToInt(MOCK_IPV4_ADDR));
+        assertEquals(0x0a000002, ApfFilter.bytesToInt(ANOTHER_IPV4_ADDR));
+        assertEquals(0x0a001fff, ApfFilter.bytesToInt(MOCK_BROADCAST_IPV4_ADDR));
+        assertEquals(0xe0000001, ApfFilter.bytesToInt(MOCK_MULTICAST_IPV4_ADDR));
+    }
+
+    public void testBroadcastAddress() throws Exception {
+        assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
+        assertEqualsIp("0.0.0.0", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 32));
+        assertEqualsIp("0.0.3.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 22));
+        assertEqualsIp("0.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 8));
+
+        assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 0));
+        assertEqualsIp("10.0.0.1", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 32));
+        assertEqualsIp("10.0.0.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 24));
+        assertEqualsIp("10.0.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 16));
+    }
+
+    public void assertEqualsIp(String expected, int got) throws Exception {
+        int want = ApfFilter.bytesToInt(InetAddress.getByName(expected).getAddress());
+        assertEquals(want, got);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index be1642e..de90de8 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -25,6 +25,7 @@
 
 import static org.mockito.Mockito.mock;
 
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -137,7 +138,8 @@
 
         @Override
         public Object getSystemService(String name) {
-            if (name == Context.CONNECTIVITY_SERVICE) return mCm;
+            if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
+            if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class);
             return super.getSystemService(name);
         }
 
@@ -717,6 +719,7 @@
     }
 
     public void tearDown() throws Exception {
+        setMobileDataAlwaysOn(false);
         if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); }
         if (mWiFiNetworkAgent != null) { mWiFiNetworkAgent.disconnect(); }
         mCellNetworkAgent = mWiFiNetworkAgent = null;
@@ -1820,6 +1823,85 @@
         mCm.unregisterNetworkCallback(cellNetworkCallback);
     }
 
+    private void setMobileDataAlwaysOn(boolean enable) {
+        ContentResolver cr = mServiceContext.getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
+        mService.updateMobileDataAlwaysOn();
+        mService.waitForIdle();
+    }
+
+    private boolean isForegroundNetwork(MockNetworkAgent network) {
+        NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
+        assertNotNull(nc);
+        return nc.hasCapability(NET_CAPABILITY_FOREGROUND);
+    }
+
+    @SmallTest
+    public void testBackgroundNetworks() throws Exception {
+        // Create a background request. We can't do this ourselves because ConnectivityService
+        // doesn't have an API for it. So just turn on mobile data always on.
+        setMobileDataAlwaysOn(true);
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        final NetworkRequest fgRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_FOREGROUND).build();
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback fgCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+        mCm.registerNetworkCallback(fgRequest, fgCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        assertTrue(isForegroundNetwork(mCellNetworkAgent));
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+
+        // When wifi connects, cell lingers.
+        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        fgCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        assertTrue(isForegroundNetwork(mCellNetworkAgent));
+        assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
+
+        // When lingering is complete, cell is still there but is now in the background.
+        fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, TEST_LINGER_DELAY_MS);
+        callback.assertNoCallback();
+        assertFalse(isForegroundNetwork(mCellNetworkAgent));
+        assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
+
+        // File a cell request and check that cell comes into the foreground.
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        final TestNetworkCallback cellCallback = new TestNetworkCallback();
+        mCm.requestNetwork(cellRequest, cellCallback);
+        cellCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        callback.assertNoCallback();  // Because the network is already up.
+        assertTrue(isForegroundNetwork(mCellNetworkAgent));
+        assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
+
+        // Release the request. The network immediately goes into the background, since it was not
+        // lingering.
+        mCm.unregisterNetworkCallback(cellCallback);
+        fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        callback.assertNoCallback();
+        assertFalse(isForegroundNetwork(mCellNetworkAgent));
+        assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
+
+        // Disconnect wifi and check that cell is foreground again.
+        mWiFiNetworkAgent.disconnect();
+        callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        assertTrue(isForegroundNetwork(mCellNetworkAgent));
+
+        mCm.unregisterNetworkCallback(callback);
+        mCm.unregisterNetworkCallback(fgCallback);
+    }
+
     @SmallTest
     public void testRequestBenchmark() throws Exception {
         // Benchmarks connecting and switching performance in the presence of a large number of
@@ -1925,8 +2007,7 @@
 
         // Turn on mobile data always on. The factory starts looking again.
         testFactory.expectAddRequests(1);
-        Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, 1);
-        mService.updateMobileDataAlwaysOn();
+        setMobileDataAlwaysOn(true);
         testFactory.waitForNetworkRequests(2);
         assertTrue(testFactory.getMyStartRequested());
 
@@ -1946,8 +2027,7 @@
 
         // Turn off mobile data always on and expect the request to disappear...
         testFactory.expectRemoveRequests(1);
-        Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, 0);
-        mService.updateMobileDataAlwaysOn();
+        setMobileDataAlwaysOn(false);
         testFactory.waitForNetworkRequests(1);
 
         // ...  and cell data to be torn down.
@@ -1959,6 +2039,79 @@
         handlerThread.quit();
     }
 
+    @SmallTest
+    public void testAvoidBadWifiSetting() throws Exception {
+        ContentResolver cr = mServiceContext.getContentResolver();
+
+        // File a request for cell to ensure it doesn't go down.
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
+        TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        NetworkRequest validatedWifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .addCapability(NET_CAPABILITY_VALIDATED)
+                .build();
+        TestNetworkCallback validatedWifiCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
+
+        // Takes effect on every rematch.
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
+
+        // Bring up validated cell.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        Network cellNetwork = mCellNetworkAgent.getNetwork();
+
+        // Bring up validated wifi.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
+
+        // Fail validation on wifi.
+        mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
+        mCm.reportNetworkConnectivity(wifiNetwork, false);
+        validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+        // Because avoid bad wifi is off, we don't switch to cellular.
+        defaultCallback.assertNoCallback();
+        assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
+                NET_CAPABILITY_VALIDATED));
+        assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
+                NET_CAPABILITY_VALIDATED));
+        assertEquals(mCm.getActiveNetwork(), wifiNetwork);
+
+        // Simulate the user selecting "switch" on the dialog.
+        Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
+        mService.updateNetworkAvoidBadWifi();
+
+        // We now switch to cell.
+        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
+                NET_CAPABILITY_VALIDATED));
+        assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
+                NET_CAPABILITY_VALIDATED));
+        assertEquals(mCm.getActiveNetwork(), cellNetwork);
+
+        // If cell goes down, we switch to wifi.
+        mCellNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        validatedWifiCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(cellNetworkCallback);
+        mCm.unregisterNetworkCallback(validatedWifiCallback);
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
     /**
      * Validate that a satisfied network request does not trigger onUnavailable() once the
      * time-out period expires.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
index 42d3b07..9ccf290 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
@@ -41,6 +41,7 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 @RunWith(AndroidJUnit4.class)
@@ -55,12 +56,16 @@
 
     AccessibilityCache mAccessibilityCache;
     AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
+    AtomicInteger numA11yNodeInfosInUse = new AtomicInteger(0);
+    AtomicInteger numA11yWinInfosInUse = new AtomicInteger(0);
 
     @Before
     public void setUp() {
         mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
         when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
         mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
+        AccessibilityNodeInfo.setNumInstancesInUseCounter(numA11yNodeInfosInUse);
+        AccessibilityWindowInfo.setNumInstancesInUseCounter(numA11yWinInfosInUse);
     }
 
     @After
@@ -68,8 +73,8 @@
         // Make sure we're recycling all of our window and node infos
         mAccessibilityCache.clear();
         AccessibilityInteractionClient.getInstance().clearCache();
-        assertEquals(0, AccessibilityWindowInfo.getNumInstancesInUse());
-        assertEquals(0, AccessibilityNodeInfo.getNumInstancesInUse());
+        assertEquals(0, numA11yWinInfosInUse.get());
+        assertEquals(0, numA11yNodeInfosInUse.get());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index f1f708c..5fe000a 100644
--- a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.notification;
 
+import com.android.server.lights.Light;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -22,7 +25,10 @@
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
+import android.app.NotificationManager;
 import android.content.Context;
+import android.app.NotificationChannel;
+import android.graphics.Color;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
@@ -30,6 +36,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.Vibrator;
+import android.provider.Settings;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
@@ -57,6 +64,8 @@
     @Mock AudioManager mAudioManager;
     @Mock Vibrator mVibrator;
     @Mock android.media.IRingtonePlayer mRingtonePlayer;
+    @Mock StatusBarManagerInternal mStatusBar;
+    @Mock Light mLight;
     @Mock Handler mHandler;
 
     private NotificationManagerService mService;
@@ -69,6 +78,15 @@
     private int mScore = 10;
     private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
 
+    private static final long[] CUSTOM_VIBRATION = new long[] {
+            300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+            300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+            300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 };
+    private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
+    private static final int CUSTOM_LIGHT_COLOR = Color.BLACK;
+    private static final int CUSTOM_LIGHT_ON = 10000;
+    private static final int CUSTOM_LIGHT_OFF = 10000;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -83,6 +101,9 @@
         mService.setVibrator(mVibrator);
         mService.setSystemReady(true);
         mService.setHandler(mHandler);
+        mService.setStatusBarManager(mStatusBar);
+        mService.setLights(mLight);
+        mService.setScreenOn(false);
         mService.setSystemNotificationSound("beep!");
     }
 
@@ -92,56 +113,85 @@
 
     private NotificationRecord getNoisyOtherNotification() {
         return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
-                true /* noisy */, true /* buzzy*/);
+                true /* noisy */, true /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getBeepyNotification() {
         return getNotificationRecord(mId, false /* insistent */, false /* once */,
-                true /* noisy */, false /* buzzy*/);
+                true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getBeepyOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
-                true /* noisy */, false /* buzzy*/);
+                true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getQuietNotification() {
         return getNotificationRecord(mId, false /* insistent */, false /* once */,
-                false /* noisy */, false /* buzzy*/);
+                false /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getQuietOtherNotification() {
         return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
-                false /* noisy */, false /* buzzy*/);
+                false /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getQuietOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
-                false /* noisy */, false /* buzzy*/);
+                false /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getInsistentBeepyNotification() {
         return getNotificationRecord(mId, true /* insistent */, false /* once */,
-                true /* noisy */, false /* buzzy*/);
+                true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getBuzzyNotification() {
         return getNotificationRecord(mId, false /* insistent */, false /* once */,
-                false /* noisy */, true /* buzzy*/);
+                false /* noisy */, true /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getBuzzyOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
-                false /* noisy */, true /* buzzy*/);
+                false /* noisy */, true /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getInsistentBuzzyNotification() {
         return getNotificationRecord(mId, true /* insistent */, false /* once */,
-                false /* noisy */, true /* buzzy*/);
+                false /* noisy */, true /* buzzy*/, false /* lights */);
+    }
+
+    private NotificationRecord getLightsNotification() {
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
+                false /* noisy */, true /* buzzy*/, true /* lights */);
+    }
+
+    private NotificationRecord getCustomBuzzyOnceNotification() {
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
+                false /* noisy */, true /* buzzy*/, false /* lights */,
+                false /* defaultVibration */, true /* defaultSound */, true /* defaultLights */);
+    }
+
+    private NotificationRecord getCustomBeepyNotification() {
+        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */,
+                true /* defaultVibration */, false /* defaultSound */, true /* defaultLights */);
+    }
+
+    private NotificationRecord getCustomLightsNotification() {
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
+                false /* noisy */, true /* buzzy*/, true /* lights */,
+                true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */);
     }
 
     private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
-            boolean noisy, boolean buzzy) {
+            boolean noisy, boolean buzzy, boolean lights) {
+        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true);
+    }
+
+    private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+            boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
+            boolean defaultSound, boolean defaultLights) {
         final Builder builder = new Builder(getContext())
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -150,10 +200,25 @@
 
         int defaults = 0;
         if (noisy) {
-            defaults |= Notification.DEFAULT_SOUND;
+            if (defaultSound) {
+                defaults |= Notification.DEFAULT_SOUND;
+            } else {
+                builder.setSound(CUSTOM_SOUND);
+            }
         }
         if (buzzy) {
-            defaults |= Notification.DEFAULT_VIBRATE;
+            if (defaultVibration) {
+                defaults |= Notification.DEFAULT_VIBRATE;
+            } else {
+                builder.setVibrate(CUSTOM_VIBRATION);
+            }
+        }
+        if (lights) {
+            if (defaultLights) {
+                defaults |= Notification.DEFAULT_LIGHTS;
+            } else {
+                builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF);
+            }
         }
         builder.setDefaults(defaults);
 
@@ -163,7 +228,10 @@
         }
         StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid,
                 mScore, n, mUser, System.currentTimeMillis());
-        return new NotificationRecord(getContext(), sbn);
+        NotificationRecord r = new NotificationRecord(getContext(), sbn,
+                new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "misc"));
+        mService.addNotification(r);
+        return r;
     }
 
     //
@@ -185,6 +253,11 @@
                 eq(false), (AudioAttributes) anyObject());
     }
 
+    private void verifyCustomBeep() throws RemoteException {
+        verify(mRingtonePlayer, times(1)).playAsync(eq(CUSTOM_SOUND), (UserHandle) anyObject(),
+                eq(false), (AudioAttributes) anyObject());
+    }
+
     private void verifyNeverStopAudio() throws RemoteException {
         verify(mRingtonePlayer, never()).stopAsync();
     }
@@ -208,6 +281,11 @@
                 eq(0), (AudioAttributes) anyObject());
     }
 
+    private void verifyCustomVibrate() {
+        verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), eq(CUSTOM_VIBRATION), eq(-1),
+                (AudioAttributes) anyObject());
+    }
+
     private void verifyStopVibrate() {
         verify(mVibrator, times(1)).cancel();
     }
@@ -216,10 +294,33 @@
         verify(mVibrator, never()).cancel();
     }
 
+    private void verifyLights() {
+        verify(mStatusBar, times(1)).notificationLightPulse(anyInt(), anyInt(), anyInt());
+    }
+
+    private void verifyCustomLights() {
+        verify(mStatusBar, times(1)).notificationLightPulse(
+                eq(CUSTOM_LIGHT_COLOR), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
+    }
+
     private Context getContext() {
         return InstrumentationRegistry.getTargetContext();
     }
 
+    //
+    // Tests
+    //
+
+    @Test
+    public void testLights() throws Exception {
+        NotificationRecord r = getLightsNotification();
+        r.setImportance(NotificationManager.IMPORTANCE_DEFAULT, "for testing");
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyLights();
+    }
+
     @Test
     public void testBeep() throws Exception {
         NotificationRecord r = getBeepyNotification();
@@ -230,9 +331,40 @@
         verifyNeverVibrate();
     }
 
-    //
-    // Tests
-    //
+    @Test
+    public void testBeepFromChannel() throws Exception {
+        NotificationRecord r = getQuietNotification();
+        r.getChannel().setDefaultRingtone(Settings.System.DEFAULT_NOTIFICATION_URI);
+        r.setImportance(NotificationManager.IMPORTANCE_DEFAULT, "for testing");
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+        verifyNeverVibrate();
+    }
+
+    @Test
+    public void testVibrateFromChannel() throws Exception {
+        NotificationRecord r = getQuietNotification();
+        r.getChannel().setVibration(true);
+        r.setImportance(NotificationManager.IMPORTANCE_DEFAULT, "for testing");
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        verifyVibrate();
+    }
+
+    @Test
+    public void testLightsFromChannel() throws Exception {
+        NotificationRecord r = getQuietNotification();
+        r.setImportance(NotificationManager.IMPORTANCE_DEFAULT, "for testing");
+        r.getChannel().setLights(true);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyLights();
+    }
 
     @Test
     public void testBeepInsistently() throws Exception {
@@ -244,6 +376,36 @@
     }
 
     @Test
+    public void testChannelNoOverwriteCustomVibration() throws Exception {
+        NotificationRecord r = getCustomBuzzyOnceNotification();
+        r.getChannel().setVibration(true);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyCustomVibrate();
+    }
+
+    @Test
+    public void testChannelNoOverwriteCustomBeep() throws Exception {
+        NotificationRecord r = getCustomBeepyNotification();
+        r.getChannel().setDefaultRingtone(Settings.System.DEFAULT_RINGTONE_URI);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyCustomBeep();
+    }
+
+    @Test
+    public void testChannelNoOverwriteCustomLights() throws Exception {
+        NotificationRecord r = getCustomLightsNotification();
+        r.getChannel().setLights(true);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyCustomLights();
+    }
+
+    @Test
     public void testNoInterruptionForMin() throws Exception {
         NotificationRecord r = getBeepyNotification();
         r.setImportance(Ranking.IMPORTANCE_MIN, "foo");
@@ -393,7 +555,7 @@
     }
 
     @Test
-    public void testDemotInsistenteSoundToVibrate() throws Exception {
+    public void testDemoteInsistenteSoundToVibrate() throws Exception {
         NotificationRecord r = getInsistentBeepyNotification();
 
         // the phone is quiet
diff --git a/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java
new file mode 100644
index 0000000..3cbde1d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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 org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.NotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import static org.junit.Assert.assertEquals;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ImportanceExtractorTest {
+
+    @Mock RankingConfig mConfig;
+
+    private String mPkg = "com.android.server.notification";
+    private int mId = 1001;
+    private int mOtherId = 1002;
+    private String mTag = null;
+    private int mUid = 1000;
+    private int mPid = 2000;
+    private int mScore = 10;
+    private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    private NotificationRecord getNotificationRecord(NotificationChannel channel) {
+        final Builder builder = new Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setDefaults(Notification.DEFAULT_SOUND);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
+                mPid, mScore, n, mUser, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+        return r;
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    //
+    // Tests
+    //
+
+    @Test
+    public void testAppPreferenceChannelNone() throws Exception {
+        ImportanceExtractor extractor = new ImportanceExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
+          NotificationManager.IMPORTANCE_MIN);
+        NotificationChannel channel = new NotificationChannel("a", "a");
+        channel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
+    }
+
+    @Test
+    public void testAppPreferenceChannelPermissive() throws Exception {
+        ImportanceExtractor extractor = new ImportanceExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
+          NotificationManager.IMPORTANCE_MIN);
+        NotificationChannel channel = new NotificationChannel("a", "a");
+        channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
+    }
+
+    @Test
+    public void testAppPreferenceChannelStrict() throws Exception {
+        ImportanceExtractor extractor = new ImportanceExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
+          NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel = new NotificationChannel("a", "a");
+        channel.setImportance(NotificationManager.IMPORTANCE_MIN);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
+    }
+
+    @Test
+    public void testNoAppPreferenceChannelPreference() throws Exception {
+        ImportanceExtractor extractor = new ImportanceExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
+          NotificationManager.IMPORTANCE_UNSPECIFIED);
+        NotificationChannel channel = new NotificationChannel("a", "a");
+        channel.setImportance(NotificationManager.IMPORTANCE_MIN);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertEquals(r.getUserImportance(), NotificationManager.IMPORTANCE_MIN);
+    }
+
+    @Test
+    public void testNoPreferences() throws Exception {
+        ImportanceExtractor extractor = new ImportanceExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
+          NotificationManager.IMPORTANCE_UNSPECIFIED);
+        NotificationChannel channel = new NotificationChannel("a", "a");
+        channel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertEquals(r.getUserImportance(), 
+             NotificationManager.IMPORTANCE_UNSPECIFIED);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index e890a48..ee33dcc 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -18,19 +18,33 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import com.android.internal.util.FastXmlSerializer;
+
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
 
 import android.app.Notification;
 import android.content.Context;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.net.Uri;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 @SmallTest
@@ -70,7 +84,8 @@
                 .setWhen(1205)
                 .build();
         mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user));
+                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 
+                getDefaultChannel());
 
         mNotiGroupGSortB = new Notification.Builder(getContext())
                 .setContentTitle("B")
@@ -79,21 +94,24 @@
                 .setWhen(1200)
                 .build();
         mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user));
+                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 
+                getDefaultChannel());
 
         mNotiNoGroup = new Notification.Builder(getContext())
                 .setContentTitle("C")
                 .setWhen(1201)
                 .build();
         mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user));
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 
+                getDefaultChannel());
 
         mNotiNoGroup2 = new Notification.Builder(getContext())
                 .setContentTitle("D")
                 .setWhen(1202)
                 .build();
         mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user));
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 
+                getDefaultChannel());
 
         mNotiNoGroupSortA = new Notification.Builder(getContext())
                 .setContentTitle("E")
@@ -101,9 +119,14 @@
                 .setSortKey("A")
                 .build();
         mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user));
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 
+                getDefaultChannel());
     }
 
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name");
+    }    
+
     @Test
     public void testFindAfterRankingWithASplitGroup() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -153,4 +176,45 @@
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
         mHelper.sort(notificationList);
     }
+
+    @Test
+    public void testChannelXml() throws Exception {
+        String pkg = "com.android.server.notification";
+        int uid = 0;
+        NotificationChannel channel1 = new NotificationChannel("id1", "name1");
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2");
+        channel2.setImportance(NotificationManager.IMPORTANCE_LOW);
+        channel2.setDefaultRingtone(new Uri.Builder().scheme("test").build());
+        channel2.setLights(true);
+        channel2.setBypassDnd(true);
+        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+        mHelper.createNotificationChannel(pkg, uid, channel1);
+        mHelper.createNotificationChannel(pkg, uid, channel2);
+
+        byte[] data;
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        serializer.startTag(null, "ranking");
+        mHelper.writeXml(serializer, false);
+        serializer.endTag(null, "ranking");
+        serializer.endDocument();
+        serializer.flush();
+
+        mHelper.deleteNotificationChannel(pkg, uid, channel1.getId());
+        mHelper.deleteNotificationChannel(pkg, uid, channel2.getId());
+        mHelper.deleteNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mHelper.readXml(parser, false);
+
+        assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId()));
+        assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel2.getId()));
+        assertNotNull(
+                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 09bd12c..53c6a33 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -25,10 +25,13 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
@@ -64,6 +67,8 @@
 import java.io.PrintStream;
 import java.security.PublicKey;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
@@ -180,6 +185,10 @@
 
     private static final String PACKAGE_NAME = "com.android.bar";
     private static final String REAL_PACKAGE_NAME = "com.android.foo";
+    private static final String PARENT_PACKAGE_NAME = "com.android.bar.parent";
+    private static final String CHILD_PACKAGE_NAME_01 = "com.android.bar.child01";
+    private static final String CHILD_PACKAGE_NAME_02 = "com.android.bar.child02";
+    private static final String CHILD_PACKAGE_NAME_03 = "com.android.bar.child03";
     private static final File INITIAL_CODE_PATH =
             new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-1");
     private static final File UPDATED_CODE_PATH =
@@ -187,20 +196,177 @@
     private static final int INITIAL_VERSION_CODE = 10023;
     private static final int UPDATED_VERSION_CODE = 10025;
 
-    /** Update existing package; don't install */
     @Test
-    public void testUpdatePackageSetting01()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting pkgSetting01 = createPackageSetting(0 /*pkgFlags*/);
-        final PackageSetting oldPkgSetting01 = new PackageSetting(pkgSetting01);
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                pkgSetting01,
+    public void testPackageStateCopy01() {
+        final List<String> childPackageNames = new ArrayList<>();
+        childPackageNames.add(CHILD_PACKAGE_NAME_01);
+        childPackageNames.add(CHILD_PACKAGE_NAME_02);
+        childPackageNames.add(CHILD_PACKAGE_NAME_03);
+        final PackageSetting origPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME,
-                null /*realPkgName*/,
-                null /*originalPkg*/,
+                REAL_PACKAGE_NAME,
+                INITIAL_CODE_PATH /*codePath*/,
+                INITIAL_CODE_PATH /*resourcePath*/,
+                null /*legacyNativeLibraryPathString*/,
+                "x86_64" /*primaryCpuAbiString*/,
+                "x86" /*secondaryCpuAbiString*/,
+                null /*cpuAbiOverrideString*/,
+                INITIAL_VERSION_CODE,
+                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
+                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
+                PARENT_PACKAGE_NAME,
+                childPackageNames,
+                0);
+        final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
+        verifySettingCopy(origPkgSetting01, testPkgSetting01);
+    }
+
+    @Test
+    public void testPackageStateCopy02() {
+        final List<String> childPackageNames = new ArrayList<>();
+        childPackageNames.add(CHILD_PACKAGE_NAME_01);
+        childPackageNames.add(CHILD_PACKAGE_NAME_02);
+        childPackageNames.add(CHILD_PACKAGE_NAME_03);
+        final PackageSetting origPkgSetting01 = new PackageSetting(
+                PACKAGE_NAME /*pkgName*/,
+                REAL_PACKAGE_NAME /*realPkgName*/,
+                INITIAL_CODE_PATH /*codePath*/,
+                INITIAL_CODE_PATH /*resourcePath*/,
+                null /*legacyNativeLibraryPathString*/,
+                "x86_64" /*primaryCpuAbiString*/,
+                "x86" /*secondaryCpuAbiString*/,
+                null /*cpuAbiOverrideString*/,
+                INITIAL_VERSION_CODE,
+                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
+                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
+                PARENT_PACKAGE_NAME,
+                childPackageNames,
+                0);
+        final PackageSetting testPkgSetting01 = new PackageSetting(
+                PACKAGE_NAME /*pkgName*/,
+                REAL_PACKAGE_NAME /*realPkgName*/,
+                UPDATED_CODE_PATH /*codePath*/,
+                UPDATED_CODE_PATH /*resourcePath*/,
+                null /*legacyNativeLibraryPathString*/,
+                null /*primaryCpuAbiString*/,
+                null /*secondaryCpuAbiString*/,
+                null /*cpuAbiOverrideString*/,
+                UPDATED_VERSION_CODE,
+                0 /*pkgFlags*/,
+                0 /*pkgPrivateFlags*/,
+                null /*parentPkgName*/,
+                null /*childPkgNames*/,
+                0);
+        testPkgSetting01.copyFrom(origPkgSetting01);
+        verifySettingCopy(origPkgSetting01, testPkgSetting01);
+    }
+
+    /** Update package */
+    @Test
+    public void testUpdatePackageSetting01() throws PackageManagerException {
+        final PackageSetting testPkgSetting01 =
+                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
+        testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
+        assertThat(testPkgSetting01.pkgFlags, is(0));
+        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
+        Settings.updatePackageSetting(
+                testPkgSetting01,
                 null /*disabledPkg*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
+                null /*legacyNativeLibraryPath*/,
+                "arm64-v8a" /*primaryCpuAbi*/,
+                "armeabi" /*secondaryCpuAbi*/,
+                0 /*pkgFlags*/,
+                0 /*pkgPrivateFlags*/,
+                null /*childPkgNames*/,
+                UserManagerService.getInstance());
+        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
+        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+        assertThat(testPkgSetting01.origPackage, is(nullValue()));
+        assertThat(testPkgSetting01.pkgFlags, is(0));
+        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        final PackageUserState userState = testPkgSetting01.readUserState(0);
+        final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
+        verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
+                false /*stopped*/, false /*installed*/);
+    }
+
+    /** Update package; package now on /system, install for user '0' */
+    @Test
+    public void testUpdatePackageSetting02() throws PackageManagerException {
+        final PackageSetting testPkgSetting01 =
+                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
+        testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
+        assertThat(testPkgSetting01.pkgFlags, is(0));
+        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
+        Settings.updatePackageSetting(
+                testPkgSetting01,
+                null /*disabledPkg*/,
+                null /*sharedUser*/,
+                UPDATED_CODE_PATH /*codePath*/,
+                null /*legacyNativeLibraryPath*/,
+                "arm64-v8a" /*primaryCpuAbi*/,
+                "armeabi" /*secondaryCpuAbi*/,
+                ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
+                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
+                null /*childPkgNames*/,
+                UserManagerService.getInstance());
+        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
+        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+        assertThat(testPkgSetting01.origPackage, is(nullValue()));
+        assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
+        assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+        final PackageUserState userState = testPkgSetting01.readUserState(0);
+        final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
+        // WARNING: When creating a shallow copy of the PackageSetting we do NOT create
+        // new contained objects. For example, this means that changes to the user state
+        // in testPkgSetting01 will also change the user state in its copy.
+        verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
+                false /*stopped*/, true /*installed*/);
+    }
+
+    /** Update package; changing shared user throws exception */
+    @Test
+    public void testUpdatePackageSetting03() {
+        final Settings testSettings01 = new Settings(new Object() /*lock*/);
+        final SharedUserSetting testUserSetting01 = createSharedUserSetting(
+                testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
+        final PackageSetting testPkgSetting01 =
+                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
+        try {
+            Settings.updatePackageSetting(
+                    testPkgSetting01,
+                    null /*disabledPkg*/,
+                    testUserSetting01 /*sharedUser*/,
+                    UPDATED_CODE_PATH /*codePath*/,
+                    null /*legacyNativeLibraryPath*/,
+                    "arm64-v8a" /*primaryCpuAbi*/,
+                    "armeabi" /*secondaryCpuAbi*/,
+                    0 /*pkgFlags*/,
+                    0 /*pkgPrivateFlags*/,
+                    null /*childPkgNames*/,
+                    UserManagerService.getInstance());
+            fail("Expected a PackageManagerException");
+        } catch (PackageManagerException expected) {
+        }
+    }
+
+    /** Create a new PackageSetting based on an original package setting */
+    @Test
+    public void testCreateNewSetting01() {
+        final PackageSetting originalPkgSetting01 =
+                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
+        final PackageSignatures originalSignatures = originalPkgSetting01.signatures;
+        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
+                REAL_PACKAGE_NAME,
+                originalPkgSetting01 /*originalPkg*/,
+                null /*disabledPkg*/,
+                null /*realPkgName*/,
+                null /*sharedUser*/,
+                UPDATED_CODE_PATH /*codePath*/,
                 UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
@@ -213,36 +379,37 @@
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(pkgSetting01));
-        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
+        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        final PackageUserState oldUserState = oldPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, oldUserState, false /*userStateChanged*/);
+        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
+        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+        assertSame(testPkgSetting01.origPackage, originalPkgSetting01);
+        // signatures object must be different
+        assertNotSame(testPkgSetting01.signatures, originalSignatures);
+        assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
+        final PackageUserState userState = testPkgSetting01.readUserState(0);
+        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
+                false /*notLaunched*/, false /*stopped*/, true /*installed*/);
     }
 
-    /** Update existing package; install for UserHandle.SYSTEM */
+    /** Create a new non-system PackageSetting */
     @Test
-    public void testUpdatePackageSetting02()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting pkgSetting01 = createPackageSetting(0 /*pkgFlags*/);
-        final PackageSetting oldPkgSetting01 = new PackageSetting(pkgSetting01);
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                pkgSetting01,
+    public void testCreateNewSetting02() {
+        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                 PACKAGE_NAME,
-                null /*realPkgName*/,
                 null /*originalPkg*/,
                 null /*disabledPkg*/,
+                null /*realPkgName*/,
                 null /*sharedUser*/,
-                UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
+                INITIAL_CODE_PATH /*codePath*/,
+                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
-                "arm64-v8a" /*primaryCpuAbi*/,
-                "armeabi" /*secondaryCpuAbi*/,
-                UPDATED_VERSION_CODE /*versionCode*/,
+                "x86_64" /*primaryCpuAbiString*/,
+                "x86" /*secondaryCpuAbiString*/,
+                INITIAL_VERSION_CODE /*versionCode*/,
                 0 /*pkgFlags*/,
                 0 /*pkgPrivateFlags*/,
                 UserHandle.SYSTEM /*installUser*/,
@@ -250,104 +417,74 @@
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(pkgSetting01));
-        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+        assertThat(testPkgSetting01.appId, is(0));
+        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        final PackageUserState oldUserState = oldPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        // The user state won't be changed in this scenario; the default user state is for
-        // the package to be installed.
-        verifyUserState(userState, oldUserState, false /*userStateChanged*/,
-                false /*notLaunched*/, false /*stopped*/);
-    }
-
-    /** Update existing package; install for {@code null} */
-    @Test
-    public void testUpdatePackageSetting03()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting pkgSetting01 = createPackageSetting(0 /*pkgFlags*/);
-        final PackageSetting oldPkgSetting01 = new PackageSetting(pkgSetting01);
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                pkgSetting01,
-                PACKAGE_NAME,
-                null /*realPkgName*/,
-                null /*originalPkg*/,
-                null /*disabledPkg*/,
-                null /*sharedUser*/,
-                UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
-                null /*legacyNativeLibraryPath*/,
-                "arm64-v8a" /*primaryCpuAbi*/,
-                "armeabi" /*secondaryCpuAbi*/,
-                UPDATED_VERSION_CODE /*versionCode*/,
-                0 /*pkgFlags*/,
-                0 /*pkgPrivateFlags*/,
-                null /*installUser*/,
-                true /*allowInstall*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
-                UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(pkgSetting01));
-        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        final PackageUserState oldUserState = oldPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, oldUserState, false /*userStateChanged*/);
-    }
-
-    /** Update renamed package */
-    @Test
-    public void testUpdatePackageSetting04()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting originalPkgSetting = createPackageSetting(0 /*pkgFlags*/);
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                null /*pkgSetting*/,
-                PACKAGE_NAME,
-                null /*realPkgName*/,
-                originalPkgSetting /*originalPkg*/,
-                null /*disabledPkg*/,
-                null /*sharedUser*/,
-                UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
-                null /*legacyNativeLibraryPath*/,
-                "arm64-v8a" /*primaryCpuAbi*/,
-                "armeabi" /*secondaryCpuAbi*/,
-                UPDATED_VERSION_CODE /*versionCode*/,
-                0 /*pkgFlags*/,
-                0 /*pkgPrivateFlags*/,
-                UserHandle.SYSTEM /*installUser*/,
-                false /*allowInstall*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
-                UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(not(originalPkgSetting)));
-        // ABI isn't pulled from the original package setting
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
+        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
-        assertThat(testPkgSetting01.origPackage, is(originalPkgSetting));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, PackageSettingBase.DEFAULT_USER_STATE /*oldUserState*/,
-                false /*userStateChanged*/);
+        assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
+        // by default, the package is considered stopped
+        final PackageUserState userState = testPkgSetting01.readUserState(0);
+        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
+                true /*notLaunched*/, true /*stopped*/, true /*installed*/);
     }
 
-    /** Update new package */
+    /** Create PackageSetting for a shared user */
     @Test
-    public void testUpdatePackageSetting05()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                null /*pkgSetting*/,
+    public void testCreateNewSetting03() {
+        final Settings testSettings01 = new Settings(new Object() /*lock*/);
+        final SharedUserSetting testUserSetting01 = createSharedUserSetting(
+                testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
+        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                 PACKAGE_NAME,
-                null /*realPkgName*/,
                 null /*originalPkg*/,
                 null /*disabledPkg*/,
+                null /*realPkgName*/,
+                testUserSetting01 /*sharedUser*/,
+                INITIAL_CODE_PATH /*codePath*/,
+                INITIAL_CODE_PATH /*resourcePath*/,
+                null /*legacyNativeLibraryPath*/,
+                "x86_64" /*primaryCpuAbiString*/,
+                "x86" /*secondaryCpuAbiString*/,
+                INITIAL_VERSION_CODE /*versionCode*/,
+                0 /*pkgFlags*/,
+                0 /*pkgPrivateFlags*/,
+                null /*installUser*/,
+                false /*allowInstall*/,
+                null /*parentPkgName*/,
+                null /*childPkgNames*/,
+                UserManagerService.getInstance());
+        assertThat(testPkgSetting01.appId, is(10064));
+        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
+        assertThat(testPkgSetting01.origPackage, is(nullValue()));
+        assertThat(testPkgSetting01.pkgFlags, is(0));
+        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
+        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
+        assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
+        final PackageUserState userState = testPkgSetting01.readUserState(0);
+        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
+                false /*notLaunched*/, false /*stopped*/, true /*installed*/);
+    }
+
+    /** Create a new PackageSetting based on a disabled package setting */
+    @Test
+    public void testCreateNewSetting04() {
+        final PackageSetting disabledPkgSetting01 =
+                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
+        disabledPkgSetting01.appId = 10064;
+        final PackageSignatures disabledSignatures = disabledPkgSetting01.signatures;
+        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
+                PACKAGE_NAME,
+                null /*originalPkg*/,
+                disabledPkgSetting01 /*disabledPkg*/,
+                null /*realPkgName*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
                 UPDATED_CODE_PATH /*resourcePath*/,
@@ -362,144 +499,34 @@
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(notNullValue()));
-        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+        assertThat(testPkgSetting01.appId, is(10064));
+        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, PackageSettingBase.DEFAULT_USER_STATE /*oldUserState*/,
-                false /*userStateChanged*/);
-    }
-
-    /** Update new package; install for {@code null} user */
-    @Test
-    public void testUpdatePackageSetting06()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                null /*pkgSetting*/,
-                PACKAGE_NAME,
-                null /*realPkgName*/,
-                null /*originalPkg*/,
-                null /*disabledPkg*/,
-                null /*sharedUser*/,
-                UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
-                null /*legacyNativeLibraryPath*/,
-                "arm64-v8a" /*primaryCpuAbi*/,
-                "armeabi" /*secondaryCpuAbi*/,
-                UPDATED_VERSION_CODE /*versionCode*/,
-                0 /*pkgFlags*/,
-                0 /*pkgPrivateFlags*/,
-                null /*installUser*/,
-                true /*allowInstall*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
-                UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(notNullValue()));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
+        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, PackageSettingBase.DEFAULT_USER_STATE /*oldUserState*/,
-                true /*userStateChanged*/, true /*notLaunched*/, true /*stopped*/);
-    }
-
-    /** Update new package; install for UserHandle.SYSTEM */
-    @Test
-    public void testUpdatePackageSetting07()
-            throws ReflectiveOperationException, PackageManagerException {
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                null /*pkgSetting*/,
-                PACKAGE_NAME,
-                null /*realPkgName*/,
-                null /*originalPkg*/,
-                null /*disabledPkg*/,
-                null /*sharedUser*/,
-                UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
-                null /*legacyNativeLibraryPath*/,
-                "arm64-v8a" /*primaryCpuAbi*/,
-                "armeabi" /*secondaryCpuAbi*/,
-                UPDATED_VERSION_CODE /*versionCode*/,
-                0 /*pkgFlags*/,
-                0 /*pkgPrivateFlags*/,
-                UserHandle.SYSTEM /*installUser*/,
-                true /*allowInstall*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
-                UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(notNullValue()));
-        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, PackageSettingBase.DEFAULT_USER_STATE /*oldUserState*/,
-                true /*userStateChanged*/, true /*notLaunched*/, true /*stopped*/);
-    }
-
-    /** Update package, but, shared user changed; ensure old setting not modified */
-    @Test
-    public void testUpdatePackageSetting08()
-            throws ReflectiveOperationException, PackageManagerException {
-        final SharedUserSetting testSharedUser =
-                new SharedUserSetting("testSharedUser", 0 /*pkgFlags*/, 0 /*_pkgPrivateFlags*/);
-        testSharedUser.userId = Process.FIRST_APPLICATION_UID + 9995;
-        final PackageSetting pkgSetting01 = createPackageSetting(0 /*pkgFlags*/);
-        final PackageSetting oldPkgSetting01 = new PackageSetting(pkgSetting01);
-        final PackageSetting testPkgSetting01 = Settings.updatePackageSetting(
-                pkgSetting01,
-                PACKAGE_NAME,
-                null /*realPkgName*/,
-                null /*originalPkg*/,
-                null /*disabledPkg*/,
-                testSharedUser /*sharedUser*/,
-                UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
-                null /*legacyNativeLibraryPath*/,
-                "arm64-v8a" /*primaryCpuAbi*/,
-                "armeabi" /*secondaryCpuAbi*/,
-                UPDATED_VERSION_CODE /*versionCode*/,
-                0 /*pkgFlags*/,
-                0 /*pkgPrivateFlags*/,
-                UserHandle.SYSTEM /*installUser*/,
-                true /*allowInstall*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
-                UserManagerService.getInstance());
-        assertThat(testPkgSetting01, is(not(pkgSetting01)));
-        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
-        assertThat(testPkgSetting01.appId, is(19995));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
-        // package setting should not have been modified
-        assertThat(pkgSetting01.primaryCpuAbiString, is("x86_64"));
-        assertThat(pkgSetting01.secondaryCpuAbiString, is("x86"));
-        final PackageUserState userState = testPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        final PackageUserState oldUserState = oldPkgSetting01.readUserState(UserHandle.USER_SYSTEM);
-        verifyUserState(userState, oldUserState, true /*userStateChanged*/,
-                true /*notLaunched*/, true /*stopped*/);
+        assertNotSame(testPkgSetting01.signatures, disabledSignatures);
+        assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
+        final PackageUserState userState = testPkgSetting01.readUserState(0);
+        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
+                false /*notLaunched*/, false /*stopped*/, true /*installed*/);
     }
 
     private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
             boolean userStateChanged) {
         verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/,
-                false /*stopped*/);
+                false /*stopped*/, true /*installed*/);
     }
 
     private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
-            boolean userStateChanged, boolean notLaunched, boolean stopped) {
+            boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) {
         assertThat(userState.blockUninstall, is(false));
         assertThat(userState.enabled, is(0));
         assertThat(userState.hidden, is(false));
-        assertThat(userState.installed, is(true));
+        assertThat(userState.installed, is(installed));
         assertThat(userState.notLaunched, is(notLaunched));
         assertThat(userState.stopped, is(stopped));
         assertThat(userState.suspended, is(false));
@@ -508,7 +535,81 @@
         }
     }
 
-    private PackageSetting createPackageSetting(int pkgFlags) {
+    private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
+        assertThat(origPkgSetting, is(not(testPkgSetting)));
+        assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
+        // different but equal objects
+        assertNotSame(origPkgSetting.childPackageNames, testPkgSetting.childPackageNames);
+        assertThat(origPkgSetting.childPackageNames, is(testPkgSetting.childPackageNames));
+        assertSame(origPkgSetting.codePath, testPkgSetting.codePath);
+        assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath));
+        assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString);
+        assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString));
+        assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
+        assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
+        assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
+        assertSame(origPkgSetting.installerPackageName, testPkgSetting.installerPackageName);
+        assertThat(origPkgSetting.installerPackageName, is(testPkgSetting.installerPackageName));
+        assertThat(origPkgSetting.installPermissionsFixed,
+                is(testPkgSetting.installPermissionsFixed));
+        assertThat(origPkgSetting.installStatus, is(testPkgSetting.installStatus));
+        assertThat(origPkgSetting.isOrphaned, is(testPkgSetting.isOrphaned));
+        assertSame(origPkgSetting.keySetData, testPkgSetting.keySetData);
+        assertThat(origPkgSetting.keySetData, is(testPkgSetting.keySetData));
+        assertThat(origPkgSetting.lastUpdateTime, is(testPkgSetting.lastUpdateTime));
+        assertSame(origPkgSetting.legacyNativeLibraryPathString,
+                testPkgSetting.legacyNativeLibraryPathString);
+        assertThat(origPkgSetting.legacyNativeLibraryPathString,
+                is(testPkgSetting.legacyNativeLibraryPathString));
+        assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
+        assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
+        assertThat(origPkgSetting.name, is(testPkgSetting.name));
+        // oldCodePaths is _not_ copied
+        // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
+        // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
+        assertSame(origPkgSetting.origPackage, testPkgSetting.origPackage);
+        assertThat(origPkgSetting.origPackage, is(testPkgSetting.origPackage));
+        assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
+        assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
+        assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
+        // No equals() method for this object
+        // assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
+        assertThat(origPkgSetting.pkgFlags, is(testPkgSetting.pkgFlags));
+        assertThat(origPkgSetting.pkgPrivateFlags, is(testPkgSetting.pkgPrivateFlags));
+        assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString);
+        assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString));
+        assertThat(origPkgSetting.realName, is(testPkgSetting.realName));
+        assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath);
+        assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath));
+        assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString);
+        assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString));
+        assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString);
+        assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString));
+        assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser);
+        assertThat(origPkgSetting.sharedUser, is(testPkgSetting.sharedUser));
+        assertSame(origPkgSetting.signatures, testPkgSetting.signatures);
+        assertThat(origPkgSetting.signatures, is(testPkgSetting.signatures));
+        assertThat(origPkgSetting.timeStamp, is(testPkgSetting.timeStamp));
+        assertThat(origPkgSetting.uidError, is(testPkgSetting.uidError));
+        assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
+        // No equals() method for SparseArray object
+        // assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
+        assertSame(origPkgSetting.verificationInfo, testPkgSetting.verificationInfo);
+        assertThat(origPkgSetting.verificationInfo, is(testPkgSetting.verificationInfo));
+        assertThat(origPkgSetting.versionCode, is(testPkgSetting.versionCode));
+        assertSame(origPkgSetting.volumeUuid, testPkgSetting.volumeUuid);
+        assertThat(origPkgSetting.volumeUuid, is(testPkgSetting.volumeUuid));
+    }
+
+    private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
+            int sharedUserId, int pkgFlags, int pkgPrivateFlags) {
+        return settings.addSharedUserLPw(
+                userName,
+                sharedUserId,
+                pkgFlags,
+                pkgPrivateFlags);
+    }
+    private PackageSetting createPackageSetting(int sharedUserId, int pkgFlags) {
         return new PackageSetting(
                 PACKAGE_NAME,
                 REAL_PACKAGE_NAME,
@@ -523,7 +624,7 @@
                 0 /*privateFlags*/,
                 null /*parentPackageName*/,
                 null /*childPackageNames*/,
-                0 /*sharedUserId*/);
+                sharedUserId);
     }
 
     private @NonNull List<UserInfo> createFakeUsers() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 143398f..3cfdc32 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -3813,9 +3813,9 @@
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
         addPackage(CALLING_PACKAGE_2, CALLING_UID_1, 10, "sig1", "sig2");
 
-        final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage(
+        final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackageForTest(
                 mService, CALLING_PACKAGE_1, USER_0);
-        final ShortcutPackageInfo spi2 = ShortcutPackageInfo.generateForInstalledPackage(
+        final ShortcutPackageInfo spi2 = ShortcutPackageInfo.generateForInstalledPackageForTest(
                 mService, CALLING_PACKAGE_2, USER_0);
 
         checkCanRestoreTo(true, spi1, 10, "sig1");
@@ -5661,6 +5661,32 @@
                                 buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0);
                     });
         });
+        // Check the user-IDs.
+        assertEquals(USER_0,
+                mService.getUserShortcutsLocked(USER_0).getPackageShortcuts(CALLING_PACKAGE_1)
+                        .getOwnerUserId());
+        assertEquals(USER_0,
+                mService.getUserShortcutsLocked(USER_0).getPackageShortcuts(CALLING_PACKAGE_1)
+                        .getPackageUserId());
+        assertEquals(USER_P0,
+                mService.getUserShortcutsLocked(USER_P0).getPackageShortcuts(CALLING_PACKAGE_1)
+                        .getOwnerUserId());
+        assertEquals(USER_P0,
+                mService.getUserShortcutsLocked(USER_P0).getPackageShortcuts(CALLING_PACKAGE_1)
+                        .getPackageUserId());
+
+        assertEquals(USER_0,
+                mService.getUserShortcutsLocked(USER_0).getLauncherShortcuts(LAUNCHER_1, USER_0)
+                        .getOwnerUserId());
+        assertEquals(USER_0,
+                mService.getUserShortcutsLocked(USER_0).getLauncherShortcuts(LAUNCHER_1, USER_0)
+                        .getPackageUserId());
+        assertEquals(USER_P0,
+                mService.getUserShortcutsLocked(USER_P0).getLauncherShortcuts(LAUNCHER_1, USER_0)
+                        .getOwnerUserId());
+        assertEquals(USER_0,
+                mService.getUserShortcutsLocked(USER_P0).getLauncherShortcuts(LAUNCHER_1, USER_0)
+                        .getPackageUserId());
     }
 
     public void testOnApplicationActive_permission() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
new file mode 100644
index 0000000..35967fb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.UserInfo;
+import android.os.Looper;
+import android.os.UserManagerInternal;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.MediumTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.LinkedHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * <p>Run with:<pre>
+ * m FrameworksServicesTests &&
+ * adb install \
+ * -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ * adb shell am instrument -e class com.android.server.pm.UserManagerServiceIdRecyclingTest \
+ * -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * </pre>
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class UserManagerServiceIdRecyclingTest {
+    private UserManagerService mUserManagerService;
+
+    @Before
+    public void setup() {
+        // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup
+        // TODO: Remove once UMS supports proper dependency injection
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());
+    }
+
+    @Test
+    public void testUserCreateRecycleIdsAddAllThenRemove() {
+        // Add max possible users
+        for (int i = UserManagerService.MIN_USER_ID; i < UserManagerService.MAX_USER_ID; i++) {
+            int userId = mUserManagerService.getNextAvailableId();
+            assertEquals(i, userId);
+            mUserManagerService.putUserInfo(newUserInfo(userId));
+        }
+
+        assertNoNextIdAvailable("All ids should be assigned");
+        // Now remove RECENTLY_REMOVED_IDS_MAX_SIZE users in the middle
+        int startFrom = UserManagerService.MIN_USER_ID + 10000 /* arbitrary number */;
+        int lastId = startFrom + UserManagerService.MAX_RECENTLY_REMOVED_IDS_SIZE;
+        for (int i = startFrom; i < lastId; i++) {
+            removeUser(i);
+            assertNoNextIdAvailable("There is not enough recently removed users. "
+                    + "Next id should not be available. Failed at u" + i);
+        }
+
+        // Now remove first user
+        removeUser(UserManagerService.MIN_USER_ID);
+
+        // Released UserIDs should be returned in the FIFO order
+        int nextId = mUserManagerService.getNextAvailableId();
+        assertEquals(startFrom, nextId);
+    }
+
+    @Test
+    public void testUserCreateRecycleIdsOverflow() {
+        LinkedHashSet<Integer> queue = new LinkedHashSet<>();
+        // Make sure we can generate more than 2x ids without issues
+        for (int i = 0; i < UserManagerService.MAX_USER_ID * 2; i++) {
+            int userId = mUserManagerService.getNextAvailableId();
+            assertTrue("Returned id should not be recent. Id=" + userId + ". Recents=" + queue,
+                    queue.add(userId));
+            if (queue.size() > UserManagerService.MAX_RECENTLY_REMOVED_IDS_SIZE) {
+                queue.remove(queue.iterator().next());
+            }
+            mUserManagerService.putUserInfo(newUserInfo(userId));
+            removeUser(userId);
+        }
+    }
+
+    private void removeUser(int userId) {
+        mUserManagerService.removeUserInfo(userId);
+        mUserManagerService.addRemovingUserIdLocked(userId);
+    }
+
+    private void assertNoNextIdAvailable(String message) {
+        try {
+            mUserManagerService.getNextAvailableId();
+            fail(message);
+        } catch (IllegalStateException e) {
+            //OK
+        }
+    }
+
+    private static UserInfo newUserInfo(int userId) {
+        return new UserInfo(userId, "User " + userId, 0);
+    }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 32fd43a..27d9d10 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -21,12 +21,12 @@
 import org.junit.runner.RunWith;
 
 import android.content.Context;
+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 android.view.WindowManagerPolicy;
 
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -42,21 +42,17 @@
  * Run: adb shell am instrument -w -e class com.android.server.wm.AppWindowTokenTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
 @SmallTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class AppWindowTokenTests {
 
     private static WindowManagerService sWm = null;
-    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
     private final IWindow mIWindow = new TestIWindow();
 
     @Before
     public void setUp() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
-        if (sWm == null) {
-            // We only want to do this once for the test process as we don't want WM to try to
-            // register a bunch of local services again.
-            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
-        }
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 8c6b007..0afbd0c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -82,6 +82,18 @@
 public class TestWindowManagerPolicy implements WindowManagerPolicy {
     private static final String TAG = "TestWindowManagerPolicy";
 
+    private static WindowManagerService sWm = null;
+
+    static synchronized WindowManagerService getWindowManagerService(Context context) {
+        if (sWm == null) {
+            // We only want to do this once for the test process as we don't want WM to try to
+            // register a bunch of local services again.
+            sWm = WindowManagerService.main(
+                    context, null, true, false, false, new TestWindowManagerPolicy());
+        }
+        return sWm;
+    }
+
     @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index a15b74b..973f1b9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -19,12 +19,17 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import java.util.Comparator;
-import java.util.LinkedList;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -39,6 +44,7 @@
  * Run: adb shell am instrument -w -e class com.android.server.wm.WindowContainerTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
 @SmallTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class WindowContainerTests {
 
@@ -82,6 +88,31 @@
     }
 
     @Test
+    public void testAdd_AlreadyHasParent() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow();
+        final TestWindowContainer child2 = root.addChildWindow();
+
+        boolean gotException = false;
+        try {
+            child1.addChildWindow(child2);
+        } catch (IllegalArgumentException e) {
+            gotException = true;
+        }
+        assertTrue(gotException);
+
+        gotException = false;
+        try {
+            root.addChildWindow(child2);
+        } catch (IllegalArgumentException e) {
+            gotException = true;
+        }
+        assertTrue(gotException);
+    }
+
+    @Test
     public void testHasChild() throws Exception {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
         final TestWindowContainer root = builder.setLayer(0).build();
@@ -195,7 +226,7 @@
         final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true));
         final TestWindowContainer child21 = child2.addChildWindow();
 
-        assertTrue(root.isVisible());
+        assertFalse(root.isVisible());
         assertTrue(child1.isVisible());
         assertFalse(child11.isVisible());
         assertTrue(child12.isVisible());
@@ -204,7 +235,7 @@
     }
 
     @Test
-    public void testDetachChild() throws Exception {
+    public void testRemoveChild() throws Exception {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
         final TestWindowContainer root = builder.setLayer(0).build();
         final TestWindowContainer child1 = root.addChildWindow();
@@ -214,7 +245,7 @@
 
         assertTrue(root.hasChild(child2));
         assertTrue(root.hasChild(child21));
-        root.detachChild(child2);
+        root.removeChild(child2);
         assertFalse(root.hasChild(child2));
         assertFalse(root.hasChild(child21));
         assertNull(child2.getParentWindow());
@@ -223,46 +254,177 @@
         assertTrue(root.hasChild(child11));
         try {
             // Can only detach our direct children.
-            root.detachChild(child11);
+            root.removeChild(child11);
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
         assertTrue(gotException);
     }
 
+    @Test
+    public void testGetOrientation_Unset() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+        // Unspecified well because we didn't specify anything...
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+    }
+
+    @Test
+    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+
+        builder.setIsVisible(false).setLayer(-1);
+        final TestWindowContainer invisible = root.addChildWindow(builder);
+        builder.setIsVisible(true).setLayer(-2);
+        final TestWindowContainer invisibleChild1VisibleAndSet = invisible.addChildWindow(builder);
+        invisibleChild1VisibleAndSet.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        // Landscape well because the container is visible and that is what we set on it above.
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisibleChild1VisibleAndSet.getOrientation());
+        // Unset because the container isn't visible even though it has a child that thinks it is
+        // visible.
+        assertEquals(SCREEN_ORIENTATION_UNSET, invisible.getOrientation());
+        // Unspecified because we are visible and we didn't specify an orientation and there isn't
+        // a visible child.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+
+        builder.setIsVisible(true).setLayer(-3);
+        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
+        visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
+        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
+
+    }
+
+    @Test
+    public void testGetOrientation_setBehind() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+
+        builder.setIsVisible(true).setLayer(-1);
+        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
+        visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
+
+        builder.setIsVisible(true).setLayer(-2);
+        final TestWindowContainer visibleUnsetChild1VisibleSetBehind =
+                visibleUnset.addChildWindow(builder);
+        visibleUnsetChild1VisibleSetBehind.setOrientation(SCREEN_ORIENTATION_BEHIND);
+        // Setting to visible behind will be used by the parents if there isn't another other
+        // container behind this one that has an orientation set.
+        assertEquals(SCREEN_ORIENTATION_BEHIND,
+                visibleUnsetChild1VisibleSetBehind.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_BEHIND, visibleUnset.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
+    }
+
+    @Test
+    public void testGetOrientation_fillsParent() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
+
+        builder.setIsVisible(true).setLayer(-1);
+        final TestWindowContainer visibleUnset = root.addChildWindow(builder);
+        visibleUnset.setOrientation(SCREEN_ORIENTATION_BEHIND);
+
+        builder.setLayer(1).setIsVisible(true);
+        final TestWindowContainer visibleUnspecifiedRootChild = root.addChildWindow(builder);
+        visibleUnspecifiedRootChild.setFillsParent(false);
+        visibleUnspecifiedRootChild.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+        // Unset because the child doesn't fill the parent. May as well be invisible...
+        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
+        // The parent uses whatever orientation is set behind this container since it doesn't fill
+        // the parent.
+        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
+
+        // Test case of child filling its parent, but its parent isn't filling its own parent.
+        builder.setLayer(2).setIsVisible(true);
+        final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent =
+                visibleUnspecifiedRootChild.addChildWindow(builder);
+        visibleUnspecifiedRootChildChildFillsParent.setOrientation(
+                SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT,
+                visibleUnspecifiedRootChildChildFillsParent.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation());
+
+
+        visibleUnspecifiedRootChild.setFillsParent(true);
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChild.getOrientation());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation());
+    }
+
+    @Test
+    public void testCompareTo() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow();
+
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child21 = child2.addChildWindow();
+        final TestWindowContainer child22 = child2.addChildWindow();
+        final TestWindowContainer child23 = child2.addChildWindow();
+        final TestWindowContainer child221 = child22.addChildWindow();
+        final TestWindowContainer child222 = child22.addChildWindow();
+        final TestWindowContainer child223 = child22.addChildWindow();
+        final TestWindowContainer child2221 = child222.addChildWindow();
+        final TestWindowContainer child2222 = child222.addChildWindow();
+        final TestWindowContainer child2223 = child222.addChildWindow();
+
+        final TestWindowContainer root2 = builder.setLayer(0).build();
+
+        assertEquals(0, root.compareTo(root));
+        assertEquals(-1, child1.compareTo(child2));
+        assertEquals(1, child2.compareTo(child1));
+
+        boolean inTheSameTree = true;
+        try {
+            root.compareTo(root2);
+        } catch (IllegalArgumentException e) {
+            inTheSameTree = false;
+        }
+        assertFalse(inTheSameTree);
+
+        assertEquals(-1, child1.compareTo(child11));
+        assertEquals(1, child21.compareTo(root));
+        assertEquals(1, child21.compareTo(child12));
+        assertEquals(-1, child11.compareTo(child2));
+        assertEquals(1, child2221.compareTo(child11));
+        assertEquals(-1, child2222.compareTo(child223));
+        assertEquals(1, child2223.compareTo(child21));
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
-    private class TestWindowContainer extends WindowContainer {
+    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
-        private final LinkedList<String> mUsers = new LinkedList();
         private final boolean mCanDetach;
         private boolean mIsAnimating;
         private boolean mIsVisible;
-        private int mRemoveIfPossibleCount;
-        private int mRemoveImmediatelyCount;
+        private boolean mFillsParent;
 
         /**
          * Compares 2 window layers and returns -1 if the first is lesser than the second in terms
          * of z-order and 1 otherwise.
          */
-        private final Comparator<WindowContainer> mWindowSubLayerComparator = (w1, w2) -> {
-            final int layer1 = ((TestWindowContainer)w1).mLayer;
-            final int layer2 = ((TestWindowContainer)w2).mLayer;
+        private final Comparator<TestWindowContainer> mWindowSubLayerComparator = (w1, w2) -> {
+            final int layer1 = w1.mLayer;
+            final int layer2 = w2.mLayer;
             if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
-                // We insert the child window into the list ordered by the mLayer.
-                // For same layers, the negative one should go below others; the positive one should
-                // go above others.
+                // We insert the child window into the list ordered by the mLayer. For same layers,
+                // the negative one should go below others; the positive one should go above others.
                 return -1;
             }
             return 1;
         };
 
-        TestWindowContainer(int layer, LinkedList<String> users, boolean canDetach,
-                boolean isAnimating, boolean isVisible) {
+        TestWindowContainer(int layer, boolean canDetach, boolean isAnimating, boolean isVisible) {
             mLayer = layer;
-            mUsers.addAll(users);
             mCanDetach = canDetach;
             mIsAnimating = isAnimating;
             mIsVisible = isVisible;
+            mFillsParent = true;
         }
 
         TestWindowContainer getParentWindow() {
@@ -273,6 +435,11 @@
             return mChildren.size();
         }
 
+        TestWindowContainer addChildWindow(TestWindowContainer child) {
+            addChild(child, mWindowSubLayerComparator);
+            return child;
+        }
+
         TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) {
             TestWindowContainer child = childBuilder.build();
             addChild(child, mWindowSubLayerComparator);
@@ -284,7 +451,7 @@
         }
 
         TestWindowContainer getChildAt(int index) {
-            return (TestWindowContainer) mChildren.get(index);
+            return mChildren.get(index);
         }
 
         @Override
@@ -299,36 +466,31 @@
 
         @Override
         boolean isVisible() {
-            return mIsVisible || super.isVisible();
+            return mIsVisible;
         }
 
         @Override
-        void removeImmediately() {
-            super.removeImmediately();
-            mRemoveImmediatelyCount++;
+        boolean fillsParent() {
+            return mFillsParent;
         }
 
-        @Override
-        void removeIfPossible() {
-            super.removeIfPossible();
-            mRemoveIfPossibleCount++;
+        void setFillsParent(boolean fillsParent) {
+            mFillsParent = fillsParent;
         }
     }
 
     private class TestWindowContainerBuilder {
         private int mLayer;
-        private LinkedList<String> mUsers = new LinkedList();
         private boolean mCanDetach;
         private boolean mIsAnimating;
         private boolean mIsVisible;
 
-        TestWindowContainerBuilder setLayer(int layer) {
-            mLayer = layer;
-            return this;
+        public TestWindowContainerBuilder() {
+            reset();
         }
 
-        TestWindowContainerBuilder addUser(String user) {
-            mUsers.add(user);
+        TestWindowContainerBuilder setLayer(int layer) {
+            mLayer = layer;
             return this;
         }
 
@@ -349,7 +511,6 @@
 
         TestWindowContainerBuilder reset() {
             mLayer = 0;
-            mUsers.clear();
             mCanDetach = false;
             mIsAnimating = false;
             mIsVisible = false;
@@ -357,7 +518,7 @@
         }
 
         TestWindowContainer build() {
-            return new TestWindowContainer(mLayer, mUsers, mCanDetach, mIsAnimating, mIsVisible);
+            return new TestWindowContainer(mLayer, mCanDetach, mIsAnimating, mIsVisible);
         }
     }
 }
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 4b29a60..1259e0f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -21,12 +21,13 @@
 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 android.view.WindowManagerPolicy;
 
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -45,23 +46,19 @@
  * Run: adb shell am instrument -w -e class com.android.server.wm.WindowStateTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
 @SmallTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class WindowStateTests {
 
     private static WindowManagerService sWm = null;
     private WindowToken mWindowToken;
-    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
     private final IWindow mIWindow = new TestIWindow();
 
     @Before
     public void setUp() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
-        if (sWm == null) {
-            // We only want to do this once for the test process as we don't want WM to try to
-            // register a bunch of local services again.
-            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
-        }
-        mWindowToken = new WindowToken(sWm, null, 0, false);
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        mWindowToken = new WindowToken(sWm, new Binder(), 0, false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 4505254..3279886 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -21,12 +21,12 @@
 import org.junit.runner.RunWith;
 
 import android.content.Context;
+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 android.view.WindowManagerPolicy;
 
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -43,21 +43,17 @@
  * Run: adb shell am instrument -w -e class com.android.server.wm.WindowTokenTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
 @SmallTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class WindowTokenTests {
 
-    private static WindowManagerService sWm = null;
-    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
+    private WindowManagerService mWm = null;
     private final IWindow mIWindow = new TestIWindow();
 
     @Before
     public void setUp() throws Exception {
         final Context context = InstrumentationRegistry.getTargetContext();
-        if (sWm == null) {
-            // We only want to do this once for the test process as we don't want WM to try to
-            // register a bunch of local services again.
-            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
-        }
+        mWm = TestWindowManagerPolicy.getWindowManagerService(context);
     }
 
     @Test
@@ -161,15 +157,15 @@
     private WindowState createWindow(WindowState parent, int type, WindowToken token) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
 
-        return new WindowState(sWm, null, mIWindow, token, parent, 0, 0, attrs, 0,
-                sWm.getDefaultDisplayContentLocked(), 0);
+        return new WindowState(mWm, null, mIWindow, token, parent, 0, 0, attrs, 0,
+                mWm.getDefaultDisplayContentLocked(), 0);
     }
 
     /* Used so we can gain access to some protected members of the {@link WindowToken} class */
     private class TestWindowToken extends WindowToken {
 
         TestWindowToken() {
-            super(sWm, null, 0, false);
+            super(mWm, null, 0, false);
         }
 
         int getWindowsCount() {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 8284773..04104b5 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -94,7 +94,7 @@
 
     static final String TAG = "UsageStatsService";
 
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = false; // Never submit with true
     static final boolean COMPRESS_TIME = false;
 
     private static final long TEN_SECONDS = 10 * 1000;
@@ -139,8 +139,8 @@
     long mSystemTimeSnapshot;
 
     boolean mAppIdleEnabled;
-    boolean mAppIdleParoled;
-    private boolean mScreenOn;
+    boolean mAppIdleTempParoled;
+    boolean mCharging;
     private long mLastAppIdleParoledTime;
 
     private volatile boolean mPendingOneTimeCheckIdleStates;
@@ -191,7 +191,7 @@
         mAppIdleEnabled = getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_enableAutoPowerModes);
         if (mAppIdleEnabled) {
-            IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
+            IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
             deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
             deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
             getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
@@ -237,7 +237,7 @@
 
             mSystemServicesReady = true;
         } else if (phase == PHASE_BOOT_COMPLETED) {
-            setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
+            setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
         }
     }
 
@@ -284,9 +284,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (BatteryManager.ACTION_CHARGING.equals(action)
-                    || BatteryManager.ACTION_DISCHARGING.equals(action)) {
-                setAppIdleParoled(BatteryManager.ACTION_CHARGING.equals(action));
+            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                setChargingState(intent.getIntExtra("plugged", 0) != 0);
             } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
                 onDeviceIdleModeChanged();
             }
@@ -376,12 +375,21 @@
         }
     }
 
+    void setChargingState(boolean charging) {
+        synchronized (mLock) {
+            if (mCharging != charging) {
+                mCharging = charging;
+                postParoleStateChanged();
+            }
+        }
+    }
+
     /** Paroled here means temporary pardon from being inactive */
     void setAppIdleParoled(boolean paroled) {
         synchronized (mLock) {
-            if (mAppIdleParoled != paroled) {
-                mAppIdleParoled = paroled;
-                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleParoled);
+            if (mAppIdleTempParoled != paroled) {
+                mAppIdleTempParoled = paroled;
+                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
                 if (paroled) {
                     postParoleEndTimeout();
                 } else {
@@ -393,6 +401,12 @@
         }
     }
 
+    boolean isParoledOrCharging() {
+        synchronized (mLock) {
+            return mAppIdleTempParoled || mCharging;
+        }
+    }
+
     private void postNextParoleTimeout() {
         if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
         mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
@@ -495,7 +509,7 @@
     /** Check if it's been a while since last parole and let idle apps do some work */
     void checkParoleTimeout() {
         synchronized (mLock) {
-            if (!mAppIdleParoled) {
+            if (!mAppIdleTempParoled) {
                 final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
                 if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
                     if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
@@ -786,7 +800,7 @@
     }
 
     boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) {
-        if (mAppIdleParoled) {
+        if (isParoledOrCharging()) {
             return false;
         }
         return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
@@ -989,8 +1003,9 @@
     }
 
     void informParoleStateChanged() {
+        final boolean paroled = isParoledOrCharging();
         for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onParoleStateChanged(mAppIdleParoled);
+            listener.onParoleStateChanged(paroled);
         }
     }
 
@@ -1072,9 +1087,9 @@
 
             pw.println();
             pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
-            pw.print(" mAppIdleParoled="); pw.print(mAppIdleParoled);
-            pw.print(" mScreenOn="); pw.println(mScreenOn);
-            pw.print("mLastAppIdleParoledTime=");
+            pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
+            pw.print(" mCharging="); pw.print(mCharging);
+            pw.print(" mLastAppIdleParoledTime=");
             TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
             pw.println();
         }
@@ -1139,7 +1154,8 @@
                     break;
 
                 case MSG_PAROLE_STATE_CHANGED:
-                    if (DEBUG) Slog.d(TAG, "Parole state changed: " + mAppIdleParoled);
+                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
+                            + ", Charging state:" + mCharging);
                     informParoleStateChanged();
                     break;
 
@@ -1466,7 +1482,7 @@
 
         @Override
         public boolean isAppIdleParoleOn() {
-            return mAppIdleParoled;
+            return isParoledOrCharging();
         }
 
         @Override
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 39b74d8..220626a 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -137,7 +137,7 @@
     private final Context mContext;
     private final ContentResolver mContentResolver;
     @GuardedBy("mLock")
-    private UsbUserSettingsManager mCurrentUserSettings;
+    private UsbProfileGroupSettingsManager mCurrentSettings;
     private NotificationManager mNotificationManager;
     private final boolean mHasUsbAccessory;
     private boolean mUseUsbNotification;
@@ -221,9 +221,9 @@
                 new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED));
     }
 
-    private UsbUserSettingsManager getCurrentUserSettings() {
+    private UsbProfileGroupSettingsManager getCurrentSettings() {
         synchronized (mLock) {
-            return mCurrentUserSettings;
+            return mCurrentSettings;
         }
     }
 
@@ -258,9 +258,9 @@
         mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
     }
 
-    public void setCurrentUser(int newCurrentUserId, UsbUserSettingsManager settings) {
+    public void setCurrentUser(int newCurrentUserId, UsbProfileGroupSettingsManager settings) {
         synchronized (mLock) {
-            mCurrentUserSettings = settings;
+            mCurrentSettings = settings;
             mHandler.obtainMessage(MSG_USER_SWITCHED, newCurrentUserId, 0).sendToTarget();
         }
     }
@@ -574,7 +574,7 @@
                     Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
                     // defer accessoryAttached if system is not ready
                     if (mBootCompleted) {
-                        getCurrentUserSettings().accessoryAttached(mCurrentAccessory);
+                        getCurrentSettings().accessoryAttached(mCurrentAccessory);
                     } // else handle in boot completed
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -767,7 +767,7 @@
                 case MSG_BOOT_COMPLETED:
                     mBootCompleted = true;
                     if (mCurrentAccessory != null) {
-                        getCurrentUserSettings().accessoryAttached(mCurrentAccessory);
+                        getCurrentSettings().accessoryAttached(mCurrentAccessory);
                     }
                     if (mDebuggingManager != null) {
                         mDebuggingManager.setAdbEnabled(mAdbEnabled);
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 84703c2..b789e17 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -63,7 +63,7 @@
     private final UsbSettingsManager mSettingsManager;
 
     @GuardedBy("mLock")
-    private UsbUserSettingsManager mCurrentUserSettings;
+    private UsbProfileGroupSettingsManager mCurrentSettings;
 
     @GuardedBy("mLock")
     private ComponentName mUsbDeviceConnectionHandler;
@@ -83,15 +83,15 @@
         }
     }
 
-    public void setCurrentUserSettings(UsbUserSettingsManager settings) {
+    public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
         synchronized (mLock) {
-            mCurrentUserSettings = settings;
+            mCurrentSettings = settings;
         }
     }
 
-    private UsbUserSettingsManager getCurrentUserSettings() {
+    private UsbProfileGroupSettingsManager getCurrentUserSettings() {
         synchronized (mLock) {
-            return mCurrentUserSettings;
+            return mCurrentSettings;
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
new file mode 100644
index 0000000..cc0fb8d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -0,0 +1,1300 @@
+/*
+ * 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.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.content.res.XmlResourceParser;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import libcore.io.IoUtils;
+
+import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
+
+class UsbProfileGroupSettingsManager {
+    private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    /** Legacy settings file, before multi-user */
+    private static final File sSingleUserSettingsFile = new File(
+            "/data/system/usb_device_manager.xml");
+
+    /** The parent user (main user of the profile group) */
+    private final UserHandle mParentUser;
+
+    private final AtomicFile mSettingsFile;
+    private final boolean mDisablePermissionDialogs;
+
+    private final Context mContext;
+
+    private final PackageManager mPackageManager;
+
+    private final UserManager mUserManager;
+    private final @NonNull UsbSettingsManager mSettingsManager;
+
+    // Maps DeviceFilter to user preferred application package
+    private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
+    // Maps AccessoryFilter to user preferred application package
+    private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
+
+    private final Object mLock = new Object();
+
+    /**
+     * A package of a user.
+     */
+    @Immutable
+    private static class UserPackage {
+        /** User */
+        final @NonNull UserHandle user;
+
+        /** Package name */
+        final @NonNull String packageName;
+
+        /**
+         * Create a description of a per user package.
+         *
+         * @param packageName The name of the package
+         * @param user The user
+         */
+        private UserPackage(@NonNull String packageName, @NonNull UserHandle user) {
+            this.packageName = packageName;
+            this.user = user;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof UserPackage)) {
+                return false;
+            } else {
+                UserPackage other = (UserPackage)obj;
+
+                return user.equals(user) && packageName.equals(other.packageName);
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            int result = user.hashCode();
+            result = 31 * result + packageName.hashCode();
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return user.getIdentifier() + "/" + packageName;
+        }
+    }
+
+    // This class is used to describe a USB device.
+    // When used in HashMaps all values must be specified,
+    // but wildcards can be used for any of the fields in
+    // the package meta-data.
+    private static class DeviceFilter {
+        // USB Vendor ID (or -1 for unspecified)
+        public final int mVendorId;
+        // USB Product ID (or -1 for unspecified)
+        public final int mProductId;
+        // USB device or interface class (or -1 for unspecified)
+        public final int mClass;
+        // USB device subclass (or -1 for unspecified)
+        public final int mSubclass;
+        // USB device protocol (or -1 for unspecified)
+        public final int mProtocol;
+        // USB device manufacturer name string (or null for unspecified)
+        public final String mManufacturerName;
+        // USB device product name string (or null for unspecified)
+        public final String mProductName;
+        // USB device serial number string (or null for unspecified)
+        public final String mSerialNumber;
+
+        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+                            String manufacturer, String product, String serialnum) {
+            mVendorId = vid;
+            mProductId = pid;
+            mClass = clasz;
+            mSubclass = subclass;
+            mProtocol = protocol;
+            mManufacturerName = manufacturer;
+            mProductName = product;
+            mSerialNumber = serialnum;
+        }
+
+        public DeviceFilter(UsbDevice device) {
+            mVendorId = device.getVendorId();
+            mProductId = device.getProductId();
+            mClass = device.getDeviceClass();
+            mSubclass = device.getDeviceSubclass();
+            mProtocol = device.getDeviceProtocol();
+            mManufacturerName = device.getManufacturerName();
+            mProductName = device.getProductName();
+            mSerialNumber = device.getSerialNumber();
+        }
+
+        public static DeviceFilter read(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            int vendorId = -1;
+            int productId = -1;
+            int deviceClass = -1;
+            int deviceSubclass = -1;
+            int deviceProtocol = -1;
+            String manufacturerName = null;
+            String productName = null;
+            String serialNumber = null;
+
+            int count = parser.getAttributeCount();
+            for (int i = 0; i < count; i++) {
+                String name = parser.getAttributeName(i);
+                String value = parser.getAttributeValue(i);
+                // Attribute values are ints or strings
+                if ("manufacturer-name".equals(name)) {
+                    manufacturerName = value;
+                } else if ("product-name".equals(name)) {
+                    productName = value;
+                } else if ("serial-number".equals(name)) {
+                    serialNumber = value;
+                } else {
+                    int intValue = -1;
+                    int radix = 10;
+                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+                        // allow hex values starting with 0x or 0X
+                        radix = 16;
+                        value = value.substring(2);
+                    }
+                    try {
+                        intValue = Integer.parseInt(value, radix);
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG, "invalid number for field " + name, e);
+                        continue;
+                    }
+                    if ("vendor-id".equals(name)) {
+                        vendorId = intValue;
+                    } else if ("product-id".equals(name)) {
+                        productId = intValue;
+                    } else if ("class".equals(name)) {
+                        deviceClass = intValue;
+                    } else if ("subclass".equals(name)) {
+                        deviceSubclass = intValue;
+                    } else if ("protocol".equals(name)) {
+                        deviceProtocol = intValue;
+                    }
+                }
+            }
+            return new DeviceFilter(vendorId, productId,
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, serialNumber);
+        }
+
+        public void write(XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, "usb-device");
+            if (mVendorId != -1) {
+                serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
+            }
+            if (mProductId != -1) {
+                serializer.attribute(null, "product-id", Integer.toString(mProductId));
+            }
+            if (mClass != -1) {
+                serializer.attribute(null, "class", Integer.toString(mClass));
+            }
+            if (mSubclass != -1) {
+                serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+            }
+            if (mProtocol != -1) {
+                serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+            }
+            if (mManufacturerName != null) {
+                serializer.attribute(null, "manufacturer-name", mManufacturerName);
+            }
+            if (mProductName != null) {
+                serializer.attribute(null, "product-name", mProductName);
+            }
+            if (mSerialNumber != null) {
+                serializer.attribute(null, "serial-number", mSerialNumber);
+            }
+            serializer.endTag(null, "usb-device");
+        }
+
+        private boolean matches(int clasz, int subclass, int protocol) {
+            return ((mClass == -1 || clasz == mClass) &&
+                    (mSubclass == -1 || subclass == mSubclass) &&
+                    (mProtocol == -1 || protocol == mProtocol));
+        }
+
+        public boolean matches(UsbDevice device) {
+            if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
+            if (mProductId != -1 && device.getProductId() != mProductId) return false;
+            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+            if (mProductName != null && device.getProductName() == null) return false;
+            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+            if (mManufacturerName != null && device.getManufacturerName() != null &&
+                !mManufacturerName.equals(device.getManufacturerName())) return false;
+            if (mProductName != null && device.getProductName() != null &&
+                !mProductName.equals(device.getProductName())) return false;
+            if (mSerialNumber != null && device.getSerialNumber() != null &&
+                !mSerialNumber.equals(device.getSerialNumber())) return false;
+
+            // check device class/subclass/protocol
+            if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
+                    device.getDeviceProtocol())) return true;
+
+            // if device doesn't match, check the interfaces
+            int count = device.getInterfaceCount();
+            for (int i = 0; i < count; i++) {
+                UsbInterface intf = device.getInterface(i);
+                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
+                        intf.getInterfaceProtocol())) return true;
+            }
+
+            return false;
+        }
+
+        public boolean matches(DeviceFilter f) {
+            if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
+            if (mProductId != -1 && f.mProductId != mProductId) return false;
+            if (f.mManufacturerName != null && mManufacturerName == null) return false;
+            if (f.mProductName != null && mProductName == null) return false;
+            if (f.mSerialNumber != null && mSerialNumber == null) return false;
+            if (mManufacturerName != null && f.mManufacturerName != null &&
+                !mManufacturerName.equals(f.mManufacturerName)) return false;
+            if (mProductName != null && f.mProductName != null &&
+                !mProductName.equals(f.mProductName)) return false;
+            if (mSerialNumber != null && f.mSerialNumber != null &&
+                !mSerialNumber.equals(f.mSerialNumber)) return false;
+
+            // check device class/subclass/protocol
+            return matches(f.mClass, f.mSubclass, f.mProtocol);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            // can't compare if we have wildcard strings
+            if (mVendorId == -1 || mProductId == -1 ||
+                    mClass == -1 || mSubclass == -1 || mProtocol == -1) {
+                return false;
+            }
+            if (obj instanceof DeviceFilter) {
+                DeviceFilter filter = (DeviceFilter)obj;
+
+                if (filter.mVendorId != mVendorId ||
+                        filter.mProductId != mProductId ||
+                        filter.mClass != mClass ||
+                        filter.mSubclass != mSubclass ||
+                        filter.mProtocol != mProtocol) {
+                    return(false);
+                }
+                if ((filter.mManufacturerName != null &&
+                        mManufacturerName == null) ||
+                    (filter.mManufacturerName == null &&
+                        mManufacturerName != null) ||
+                    (filter.mProductName != null &&
+                        mProductName == null)  ||
+                    (filter.mProductName == null &&
+                        mProductName != null) ||
+                    (filter.mSerialNumber != null &&
+                        mSerialNumber == null)  ||
+                    (filter.mSerialNumber == null &&
+                        mSerialNumber != null)) {
+                    return(false);
+                }
+                if  ((filter.mManufacturerName != null &&
+                        mManufacturerName != null &&
+                        !mManufacturerName.equals(filter.mManufacturerName)) ||
+                     (filter.mProductName != null &&
+                        mProductName != null &&
+                        !mProductName.equals(filter.mProductName)) ||
+                     (filter.mSerialNumber != null &&
+                        mSerialNumber != null &&
+                        !mSerialNumber.equals(filter.mSerialNumber))) {
+                    return(false);
+                }
+                return(true);
+            }
+            if (obj instanceof UsbDevice) {
+                UsbDevice device = (UsbDevice)obj;
+                if (device.getVendorId() != mVendorId ||
+                        device.getProductId() != mProductId ||
+                        device.getDeviceClass() != mClass ||
+                        device.getDeviceSubclass() != mSubclass ||
+                        device.getDeviceProtocol() != mProtocol) {
+                    return(false);
+                }
+                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+                        (mManufacturerName == null && device.getManufacturerName() != null) ||
+                        (mProductName != null && device.getProductName() == null) ||
+                        (mProductName == null && device.getProductName() != null) ||
+                        (mSerialNumber != null && device.getSerialNumber() == null) ||
+                        (mSerialNumber == null && device.getSerialNumber() != null)) {
+                    return(false);
+                }
+                if ((device.getManufacturerName() != null &&
+                        !mManufacturerName.equals(device.getManufacturerName())) ||
+                        (device.getProductName() != null &&
+                            !mProductName.equals(device.getProductName())) ||
+                        (device.getSerialNumber() != null &&
+                            !mSerialNumber.equals(device.getSerialNumber()))) {
+                    return(false);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return (((mVendorId << 16) | mProductId) ^
+                    ((mClass << 16) | (mSubclass << 8) | mProtocol));
+        }
+
+        @Override
+        public String toString() {
+            return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
+                    ",mClass=" + mClass + ",mSubclass=" + mSubclass +
+                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+                    "]";
+        }
+    }
+
+    // This class is used to describe a USB accessory.
+    // When used in HashMaps all values must be specified,
+    // but wildcards can be used for any of the fields in
+    // the package meta-data.
+    private static class AccessoryFilter {
+        // USB accessory manufacturer (or null for unspecified)
+        public final String mManufacturer;
+        // USB accessory model (or null for unspecified)
+        public final String mModel;
+        // USB accessory version (or null for unspecified)
+        public final String mVersion;
+
+        public AccessoryFilter(String manufacturer, String model, String version) {
+            mManufacturer = manufacturer;
+            mModel = model;
+            mVersion = version;
+        }
+
+        public AccessoryFilter(UsbAccessory accessory) {
+            mManufacturer = accessory.getManufacturer();
+            mModel = accessory.getModel();
+            mVersion = accessory.getVersion();
+        }
+
+        public static AccessoryFilter read(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            String manufacturer = null;
+            String model = null;
+            String version = null;
+
+            int count = parser.getAttributeCount();
+            for (int i = 0; i < count; i++) {
+                String name = parser.getAttributeName(i);
+                String value = parser.getAttributeValue(i);
+
+                if ("manufacturer".equals(name)) {
+                    manufacturer = value;
+                } else if ("model".equals(name)) {
+                    model = value;
+                } else if ("version".equals(name)) {
+                    version = value;
+                }
+             }
+             return new AccessoryFilter(manufacturer, model, version);
+        }
+
+        public void write(XmlSerializer serializer)throws IOException {
+            serializer.startTag(null, "usb-accessory");
+            if (mManufacturer != null) {
+                serializer.attribute(null, "manufacturer", mManufacturer);
+            }
+            if (mModel != null) {
+                serializer.attribute(null, "model", mModel);
+            }
+            if (mVersion != null) {
+                serializer.attribute(null, "version", mVersion);
+            }
+            serializer.endTag(null, "usb-accessory");
+        }
+
+        public boolean matches(UsbAccessory acc) {
+            if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
+            if (mModel != null && !acc.getModel().equals(mModel)) return false;
+            if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
+            return true;
+        }
+
+        public boolean matches(AccessoryFilter f) {
+            if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
+            if (mModel != null && !f.mModel.equals(mModel)) return false;
+            if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
+            return true;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            // can't compare if we have wildcard strings
+            if (mManufacturer == null || mModel == null || mVersion == null) {
+                return false;
+            }
+            if (obj instanceof AccessoryFilter) {
+                AccessoryFilter filter = (AccessoryFilter)obj;
+                return (mManufacturer.equals(filter.mManufacturer) &&
+                        mModel.equals(filter.mModel) &&
+                        mVersion.equals(filter.mVersion));
+            }
+            if (obj instanceof UsbAccessory) {
+                UsbAccessory accessory = (UsbAccessory)obj;
+                return (mManufacturer.equals(accessory.getManufacturer()) &&
+                        mModel.equals(accessory.getModel()) &&
+                        mVersion.equals(accessory.getVersion()));
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+                    (mModel == null ? 0 : mModel.hashCode()) ^
+                    (mVersion == null ? 0 : mVersion.hashCode()));
+        }
+
+        @Override
+        public String toString() {
+            return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
+                                "\", mModel=\"" + mModel +
+                                "\", mVersion=\"" + mVersion + "\"]";
+        }
+    }
+
+    private class MyPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            handlePackageUpdate(packageName);
+        }
+
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            handlePackageUpdate(packageName);
+            return false;
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));
+        }
+    }
+
+    MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+
+    private final MtpNotificationManager mMtpNotificationManager;
+
+    /**
+     * Create new settings manager for a profile group.
+     *
+     * @param context The context of the service
+     * @param user The parent profile
+     * @param settingsManager The settings manager of the service
+     */
+    UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
+            @NonNull UsbSettingsManager settingsManager) {
+        if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
+
+        Context parentUserContext;
+        try {
+            parentUserContext = context.createPackageContextAsUser("android", 0, user);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException("Missing android package");
+        }
+
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mSettingsManager = settingsManager;
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
+        mParentUser = user;
+        mSettingsFile = new AtomicFile(new File(
+                Environment.getUserSystemDirectory(user.getIdentifier()),
+                "usb_device_manager.xml"));
+
+        mDisablePermissionDialogs = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+
+        synchronized (mLock) {
+            if (UserHandle.SYSTEM.equals(user)) {
+                upgradeSingleUserLocked();
+            }
+            readSettingsLocked();
+        }
+
+        mPackageMonitor.register(context, null, true);
+        mMtpNotificationManager = new MtpNotificationManager(
+                parentUserContext,
+                new MtpNotificationManager.OnOpenInAppListener() {
+                    @Override
+                    public void onOpenInApp(UsbDevice device) {
+                        resolveActivity(createDeviceAttachedIntent(device), device);
+                    }
+                });
+    }
+
+    private void readPreference(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        String packageName = null;
+
+        // If not set, assume it to be the parent profile
+        UserHandle user = mParentUser;
+
+        int count = parser.getAttributeCount();
+        for (int i = 0; i < count; i++) {
+            if ("package".equals(parser.getAttributeName(i))) {
+                packageName = parser.getAttributeValue(i);
+            }
+            if ("user".equals(parser.getAttributeName(i))) {
+                // Might return null if user is not known anymore
+                user = mUserManager
+                        .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i)));
+            }
+        }
+
+        XmlUtils.nextElement(parser);
+        if ("usb-device".equals(parser.getName())) {
+            DeviceFilter filter = DeviceFilter.read(parser);
+            if (user != null) {
+                mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
+            }
+        } else if ("usb-accessory".equals(parser.getName())) {
+            AccessoryFilter filter = AccessoryFilter.read(parser);
+            if (user != null) {
+                mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
+            }
+        }
+        XmlUtils.nextElement(parser);
+    }
+
+    /**
+     * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
+     * Should only by called by owner.
+     */
+    private void upgradeSingleUserLocked() {
+        if (sSingleUserSettingsFile.exists()) {
+            mDevicePreferenceMap.clear();
+            mAccessoryPreferenceMap.clear();
+
+            FileInputStream fis = null;
+            try {
+                fis = new FileInputStream(sSingleUserSettingsFile);
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(fis, StandardCharsets.UTF_8.name());
+
+                XmlUtils.nextElement(parser);
+                while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                    final String tagName = parser.getName();
+                    if ("preference".equals(tagName)) {
+                        readPreference(parser);
+                    } else {
+                        XmlUtils.nextElement(parser);
+                    }
+                }
+            } catch (IOException e) {
+                Log.wtf(TAG, "Failed to read single-user settings", e);
+            } catch (XmlPullParserException e) {
+                Log.wtf(TAG, "Failed to read single-user settings", e);
+            } finally {
+                IoUtils.closeQuietly(fis);
+            }
+
+            writeSettingsLocked();
+
+            // Success or failure, we delete single-user file
+            sSingleUserSettingsFile.delete();
+        }
+    }
+
+    private void readSettingsLocked() {
+        if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
+
+        mDevicePreferenceMap.clear();
+        mAccessoryPreferenceMap.clear();
+
+        FileInputStream stream = null;
+        try {
+            stream = mSettingsFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if ("preference".equals(tagName)) {
+                    readPreference(parser);
+                } else {
+                    XmlUtils.nextElement(parser);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            if (DEBUG) Slog.d(TAG, "settings file not found");
+        } catch (Exception e) {
+            Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
+            mSettingsFile.delete();
+        } finally {
+            IoUtils.closeQuietly(stream);
+        }
+    }
+
+    private void writeSettingsLocked() {
+        if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
+
+        FileOutputStream fos = null;
+        try {
+            fos = mSettingsFile.startWrite();
+
+            FastXmlSerializer serializer = new FastXmlSerializer();
+            serializer.setOutput(fos, StandardCharsets.UTF_8.name());
+            serializer.startDocument(null, true);
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startTag(null, "settings");
+
+            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
+                serializer.startTag(null, "preference");
+                serializer.attribute(null, "package", mDevicePreferenceMap.get(filter).packageName);
+                serializer.attribute(null, "user",
+                        String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
+                filter.write(serializer);
+                serializer.endTag(null, "preference");
+            }
+
+            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
+                serializer.startTag(null, "preference");
+                serializer.attribute(null, "package",
+                        mAccessoryPreferenceMap.get(filter).packageName);
+                serializer.attribute(null, "user",
+                        String.valueOf(getSerial(mAccessoryPreferenceMap.get(filter).user)));
+                filter.write(serializer);
+                serializer.endTag(null, "preference");
+            }
+
+            serializer.endTag(null, "settings");
+            serializer.endDocument();
+
+            mSettingsFile.finishWrite(fos);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write settings", e);
+            if (fos != null) {
+                mSettingsFile.failWrite(fos);
+            }
+        }
+    }
+
+    // Checks to see if a package matches a device or accessory.
+    // Only one of device and accessory should be non-null.
+    private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
+            UsbDevice device, UsbAccessory accessory) {
+        if (info.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+            return true;
+        }
+
+        ActivityInfo ai = info.activityInfo;
+
+        XmlResourceParser parser = null;
+        try {
+            parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
+            if (parser == null) {
+                Slog.w(TAG, "no meta-data for " + info);
+                return false;
+            }
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if (device != null && "usb-device".equals(tagName)) {
+                    DeviceFilter filter = DeviceFilter.read(parser);
+                    if (filter.matches(device)) {
+                        return true;
+                    }
+                }
+                else if (accessory != null && "usb-accessory".equals(tagName)) {
+                    AccessoryFilter filter = AccessoryFilter.read(parser);
+                    if (filter.matches(accessory)) {
+                        return true;
+                    }
+                }
+                XmlUtils.nextElement(parser);
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to load component info " + info.toString(), e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return false;
+    }
+
+    /**
+     * Resolve all activities that match an intent for all profiles of this group.
+     *
+     * @param intent The intent to resolve
+     *
+     * @return The {@link ResolveInfo}s for all profiles of the group
+     */
+    private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles(
+            @NonNull Intent intent) {
+        List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier());
+
+        ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
+        int numProfiles = profiles.size();
+        for (int i = 0; i < numProfiles; i++) {
+            resolveInfos.addAll(mPackageManager.queryIntentActivitiesAsUser(intent,
+                    PackageManager.GET_META_DATA, profiles.get(i).id));
+        }
+
+        return resolveInfos;
+    }
+
+    private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
+        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+        List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
+        int count = resolveInfos.size();
+        for (int i = 0; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
+                matches.add(resolveInfo);
+            }
+        }
+        return matches;
+    }
+
+    private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
+            UsbAccessory accessory, Intent intent) {
+        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+        List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent);
+        int count = resolveInfos.size();
+        for (int i = 0; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
+                matches.add(resolveInfo);
+            }
+        }
+        return matches;
+    }
+
+    public void deviceAttached(UsbDevice device) {
+        final Intent intent = createDeviceAttachedIntent(device);
+
+        // Send broadcast to running activities with registered intent
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+        if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
+            // Show notification if the device is MTP storage.
+            mMtpNotificationManager.showNotification(device);
+        } else {
+            resolveActivity(intent, device);
+        }
+    }
+
+    private void resolveActivity(Intent intent, UsbDevice device) {
+        ArrayList<ResolveInfo> matches;
+        String defaultPackage = null;
+        UserHandle user = null;
+        synchronized (mLock) {
+            matches = getDeviceMatchesLocked(device, intent);
+            // Launch our default activity directly, if we have one.
+            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
+            UserPackage userPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
+            if (userPackage != null) {
+                defaultPackage = userPackage.packageName;
+                user = userPackage.user;
+            }
+        }
+
+        // Start activity with registered intent
+        resolveActivity(intent, matches, defaultPackage, user, device, null);
+    }
+
+    public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
+        final Intent intent = createDeviceAttachedIntent(device);
+
+        // Send broadcast to running activity with registered intent
+        mContext.sendBroadcast(intent);
+
+        ApplicationInfo appInfo;
+        try {
+            appInfo = mPackageManager.getApplicationInfo(component.getPackageName(), 0);
+        } catch (NameNotFoundException e) {
+            Slog.e(TAG, "Default USB handling package not found: " + component.getPackageName());
+            return;
+        }
+
+        mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
+                .grantDevicePermission(device, appInfo.uid);
+
+        Intent activityIntent = new Intent(intent);
+        activityIntent.setComponent(component);
+        try {
+            mContext.startActivityAsUser(activityIntent, mParentUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "unable to start activity " + activityIntent);
+        }
+    }
+
+    /**
+     * Remove notifications for a usb device.
+     *
+     * @param device The device the notifications are for.
+     */
+    void usbDeviceRemoved(@NonNull UsbDevice device) {
+        mMtpNotificationManager.hideNotification(device.getDeviceId());
+    }
+
+    public void accessoryAttached(UsbAccessory accessory) {
+        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        ArrayList<ResolveInfo> matches;
+        String defaultPackage = null;
+        UserHandle user = null;
+        synchronized (mLock) {
+            matches = getAccessoryMatchesLocked(accessory, intent);
+            // Launch our default activity directly, if we have one.
+            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
+            UserPackage userPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
+            if (userPackage != null) {
+                defaultPackage = userPackage.packageName;
+                user = userPackage.user;
+            }
+        }
+
+        resolveActivity(intent, matches, defaultPackage, user, null, accessory);
+    }
+
+    /**
+     * Start the appropriate package when an device/accessory got attached.
+     *
+     * @param intent The intent to start the package
+     * @param matches The available resolutions of the intent
+     * @param defaultPackage The default package for the device (if set)
+     * @param defaultUser The user of the default package (if package is set)
+     * @param device The device if a device was attached
+     * @param accessory The accessory if a device was attached
+     */
+    private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
+            @Nullable String defaultPackage, @Nullable UserHandle defaultUser,
+            @Nullable UsbDevice device, @Nullable UsbAccessory accessory) {
+        int count = matches.size();
+
+        // don't show the resolver activity if there are no choices available
+        if (count == 0) {
+            if (accessory != null) {
+                String uri = accessory.getUri();
+                if (uri != null && uri.length() > 0) {
+                    // display URI to user
+                    Intent dialogIntent = new Intent();
+                    dialogIntent.setClassName("com.android.systemui",
+                            "com.android.systemui.usb.UsbAccessoryUriActivity");
+                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+                    dialogIntent.putExtra("uri", uri);
+                    try {
+                        mContext.startActivityAsUser(dialogIntent, mParentUser);
+                    } catch (ActivityNotFoundException e) {
+                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
+                    }
+                }
+            }
+
+            // do nothing
+            return;
+        }
+
+        ResolveInfo defaultRI = null;
+        if (count == 1 && defaultPackage == null) {
+            // Check to see if our single choice is on the system partition.
+            // If so, treat it as our default without calling UsbResolverActivity
+            ResolveInfo rInfo = matches.get(0);
+            if (rInfo.activityInfo != null &&
+                    rInfo.activityInfo.applicationInfo != null &&
+                    (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                defaultRI = rInfo;
+            }
+
+            if (mDisablePermissionDialogs) {
+                // bypass dialog and launch the only matching activity
+                rInfo = matches.get(0);
+                if (rInfo.activityInfo != null) {
+                    defaultPackage = rInfo.activityInfo.packageName;
+                    defaultUser = UserHandle.getUserHandleForUid(
+                            rInfo.activityInfo.applicationInfo.uid);
+                }
+            }
+        }
+
+        if (defaultRI == null && defaultPackage != null) {
+            // look for default activity
+            for (int i = 0; i < count; i++) {
+                ResolveInfo rInfo = matches.get(i);
+                if (rInfo.activityInfo != null &&
+                        defaultPackage.equals(rInfo.activityInfo.packageName) &&
+                        defaultUser.getIdentifier() ==
+                                UserHandle.getUserId(rInfo.activityInfo.applicationInfo.uid)) {
+                    defaultRI = rInfo;
+                    break;
+                }
+            }
+        }
+
+        if (defaultRI != null) {
+            UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
+                    UserHandle.getUserId(defaultRI.activityInfo.applicationInfo.uid));
+            // grant permission for default activity
+            if (device != null) {
+                defaultRIUserSettings.
+                        grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
+            } else if (accessory != null) {
+                defaultRIUserSettings.grantAccessoryPermission(accessory,
+                                defaultRI.activityInfo.applicationInfo.uid);
+            }
+
+            // start default activity directly
+            try {
+                intent.setComponent(
+                        new ComponentName(defaultRI.activityInfo.packageName,
+                                defaultRI.activityInfo.name));
+
+                UserHandle user = UserHandle.getUserHandleForUid(
+                        defaultRI.activityInfo.applicationInfo.uid);
+                mContext.startActivityAsUser(intent, user);
+            } catch (ActivityNotFoundException e) {
+                Slog.e(TAG, "startActivity failed", e);
+            }
+        } else {
+            Intent resolverIntent = new Intent();
+            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            UserHandle user;
+
+            if (count == 1) {
+                ResolveInfo rInfo = matches.get(0);
+
+                // start UsbConfirmActivity if there is only one choice
+                resolverIntent.setClassName("com.android.systemui",
+                        "com.android.systemui.usb.UsbConfirmActivity");
+                resolverIntent.putExtra("rinfo", rInfo);
+                user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
+
+                if (device != null) {
+                    resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
+                } else {
+                    resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+                }
+            } else {
+                user = mParentUser;
+
+                // start UsbResolverActivity so user can choose an activity
+                resolverIntent.setClassName("com.android.systemui",
+                        "com.android.systemui.usb.UsbResolverActivity");
+                resolverIntent.putParcelableArrayListExtra("rlist", matches);
+                resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+            }
+            try {
+                mContext.startActivityAsUser(resolverIntent, user);
+            } catch (ActivityNotFoundException e) {
+                Slog.e(TAG, "unable to start activity " + resolverIntent, e);
+            }
+        }
+    }
+
+    private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
+        boolean changed = false;
+        for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
+            if (filter.matches(test)) {
+                mDevicePreferenceMap.remove(test);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
+        boolean changed = false;
+        for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
+            if (filter.matches(test)) {
+                mAccessoryPreferenceMap.remove(test);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
+            String metaDataName) {
+        XmlResourceParser parser = null;
+        boolean changed = false;
+
+        try {
+            parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
+            if (parser == null) return false;
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if ("usb-device".equals(tagName)) {
+                    DeviceFilter filter = DeviceFilter.read(parser);
+                    if (clearCompatibleMatchesLocked(packageName, filter)) {
+                        changed = true;
+                    }
+                }
+                else if ("usb-accessory".equals(tagName)) {
+                    AccessoryFilter filter = AccessoryFilter.read(parser);
+                    if (clearCompatibleMatchesLocked(packageName, filter)) {
+                        changed = true;
+                    }
+                }
+                XmlUtils.nextElement(parser);
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return changed;
+    }
+
+    // Check to see if the package supports any USB devices or accessories.
+    // If so, clear any non-matching preferences for matching devices/accessories.
+    private void handlePackageUpdate(String packageName) {
+        synchronized (mLock) {
+            PackageInfo info;
+            boolean changed = false;
+
+            try {
+                info = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+            } catch (NameNotFoundException e) {
+                Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
+                return;
+            }
+
+            ActivityInfo[] activities = info.activities;
+            if (activities == null) return;
+            for (int i = 0; i < activities.length; i++) {
+                // check for meta-data, both for devices and accessories
+                if (handlePackageUpdateLocked(packageName, activities[i],
+                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                    changed = true;
+                }
+                if (handlePackageUpdateLocked(packageName, activities[i],
+                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+                    changed = true;
+                }
+            }
+
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    /**
+     * Get the serial number for a user handle.
+     *
+     * @param user The user handle
+     *
+     * @return The serial number
+     */
+    private int getSerial(@NonNull UserHandle user) {
+        return mUserManager.getUserSerialNumber(user.getIdentifier());
+    }
+
+    /**
+     * Set a package as default handler for a device.
+     *
+     * @param device The device that should be handled by default
+     * @param packageName The default handler package
+     * @param user The user the package belongs to
+     */
+    void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName,
+            @Nullable UserHandle user) {
+        DeviceFilter filter = new DeviceFilter(device);
+        boolean changed = false;
+        synchronized (mLock) {
+            if (packageName == null) {
+                changed = (mDevicePreferenceMap.remove(filter) != null);
+            } else {
+                changed = !packageName.equals(mDevicePreferenceMap.get(filter));
+                if (changed) {
+                    mDevicePreferenceMap.put(filter, new UserPackage(packageName, user));
+                }
+            }
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    /**
+     * Set a package as default handler for a accessory.
+     *
+     * @param accessory The accessory that should be handled by default
+     * @param packageName The default handler package
+     * @param user The user the package belongs to
+     */
+    void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName,
+            @Nullable UserHandle user) {
+        AccessoryFilter filter = new AccessoryFilter(accessory);
+        boolean changed = false;
+        synchronized (mLock) {
+            if (packageName == null) {
+                changed = (mAccessoryPreferenceMap.remove(filter) != null);
+            } else {
+                changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
+                if (changed) {
+                    mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user));
+                }
+            }
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    /**
+     * Check if a package has is the default handler for any usb device or accessory.
+     *
+     * @param packageName The package name
+     * @param user The user the package belongs to
+     *
+     * @return {@code true} iff the package is default for any usb device or accessory
+     */
+    boolean hasDefaults(@NonNull String packageName, @NonNull UserHandle user) {
+        UserPackage userPackage = new UserPackage(packageName, user);
+        synchronized (mLock) {
+            if (mDevicePreferenceMap.values().contains(userPackage)) return true;
+            if (mAccessoryPreferenceMap.values().contains(userPackage)) return true;
+            return false;
+        }
+    }
+
+    /**
+     * Clear defaults for a package from any preference.
+     *
+     * @param packageName The package to remove
+     * @param user The user the package belongs to
+     */
+    void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) {
+        UserPackage userPackage = new UserPackage(packageName, user);
+
+        synchronized (mLock) {
+            if (clearPackageDefaultsLocked(userPackage)) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    /**
+     * Clear defaults for a package from any preference (does not persist).
+     *
+     * @param userPackage The package to remove
+     *
+     * @return {@code true} iff at least one preference was cleared
+     */
+    private boolean clearPackageDefaultsLocked(@NonNull UserPackage userPackage) {
+        boolean cleared = false;
+        synchronized (mLock) {
+            if (mDevicePreferenceMap.containsValue(userPackage)) {
+                // make a copy of the key set to avoid ConcurrentModificationException
+                Object[] keys = mDevicePreferenceMap.keySet().toArray();
+                for (int i = 0; i < keys.length; i++) {
+                    Object key = keys[i];
+                    if (userPackage.equals(mDevicePreferenceMap.get(key))) {
+                        mDevicePreferenceMap.remove(key);
+                        cleared = true;
+                    }
+                }
+            }
+            if (mAccessoryPreferenceMap.containsValue(userPackage)) {
+                // make a copy of the key set to avoid ConcurrentModificationException
+                Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
+                for (int i = 0; i < keys.length; i++) {
+                    Object key = keys[i];
+                    if (userPackage.equals(mAccessoryPreferenceMap.get(key))) {
+                        mAccessoryPreferenceMap.remove(key);
+                        cleared = true;
+                    }
+                }
+            }
+            return cleared;
+        }
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("Device preferences:");
+            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
+                pw.println("  " + filter + ": " + mDevicePreferenceMap.get(filter));
+            }
+            pw.println("Accessory preferences:");
+            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
+                pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
+            }
+        }
+    }
+
+    private static Intent createDeviceAttachedIntent(UsbDevice device) {
+        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 1f1fb3d..02c7214 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -115,7 +115,7 @@
     public UsbService(Context context) {
         mContext = context;
 
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mUserManager = context.getSystemService(UserManager.class);
         mSettingsManager = new UsbSettingsManager(context);
         mAlsaManager = new UsbAlsaManager(context);
 
@@ -160,15 +160,16 @@
         synchronized (mLock) {
             mCurrentUserId = newUserId;
 
-            // The following two modules need to know about the current user. If they need to
-            // distinguish by profile of the user, the id has to be passed in the call to the
+            // The following two modules need to know about the current profile group. If they need
+            // to distinguish by profile of the user, the id has to be passed in the call to the
             // module.
-            UsbUserSettingsManager userSettings = getSettingsForUser(newUserId);
+            UsbProfileGroupSettingsManager settings =
+                    mSettingsManager.getSettingsForProfileGroup(UserHandle.of(newUserId));
             if (mHostManager != null) {
-                mHostManager.setCurrentUserSettings(userSettings);
+                mHostManager.setCurrentUserSettings(settings);
             }
             if (mDeviceManager != null) {
-                mDeviceManager.setCurrentUser(newUserId, userSettings);
+                mDeviceManager.setCurrentUser(newUserId, settings);
             }
         }
     }
@@ -284,14 +285,24 @@
 
     @Override
     public void setDevicePackage(UsbDevice device, String packageName, int userId) {
+        device = Preconditions.checkNotNull(device);
+
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        getSettingsForUser(userId).setDevicePackage(device, packageName);
+
+        UserHandle user = UserHandle.of(userId);
+        mSettingsManager.getSettingsForProfileGroup(user).setDevicePackage(device, packageName,
+                user);
     }
 
     @Override
     public void setAccessoryPackage(UsbAccessory accessory, String packageName, int userId) {
+        accessory = Preconditions.checkNotNull(accessory);
+
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        getSettingsForUser(userId).setAccessoryPackage(accessory, packageName);
+
+        UserHandle user = UserHandle.of(userId);
+        mSettingsManager.getSettingsForProfileGroup(user).setAccessoryPackage(accessory,
+                packageName, user);
     }
 
     @Override
@@ -335,14 +346,22 @@
 
     @Override
     public boolean hasDefaults(String packageName, int userId) {
+        packageName = Preconditions.checkStringNotEmpty(packageName);
+
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        return getSettingsForUser(userId).hasDefaults(packageName);
+
+        UserHandle user = UserHandle.of(userId);
+        return mSettingsManager.getSettingsForProfileGroup(user).hasDefaults(packageName, user);
     }
 
     @Override
     public void clearDefaults(String packageName, int userId) {
+        packageName = Preconditions.checkStringNotEmpty(packageName);
+
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        getSettingsForUser(userId).clearDefaults(packageName);
+
+        UserHandle user = UserHandle.of(userId);
+        mSettingsManager.getSettingsForProfileGroup(user).clearDefaults(packageName, user);
     }
 
     @Override
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 78f8477..b251d26 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -20,10 +20,12 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Slog;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
@@ -43,8 +45,18 @@
     @GuardedBy("mSettingsByUser")
     private final SparseArray<UsbUserSettingsManager> mSettingsByUser = new SparseArray<>();
 
+    /**
+     * Map from the parent profile's user id to {@link UsbProfileGroupSettingsManager} for the
+     * group.
+     */
+    @GuardedBy("mSettingsByProfileGroup")
+    private final SparseArray<UsbProfileGroupSettingsManager> mSettingsByProfileGroup
+            = new SparseArray<>();
+    private UserManager mUserManager;
+
     public UsbSettingsManager(@NonNull Context context) {
         mContext = context;
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
     }
 
     /**
@@ -66,6 +78,34 @@
     }
 
     /**
+     * Get the {@link UsbProfileGroupSettingsManager} for a user.
+     *
+     * @param user Any user of the profile group
+     *
+     * @return The settings for the profile group
+     */
+    @NonNull UsbProfileGroupSettingsManager getSettingsForProfileGroup(@NonNull UserHandle user) {
+        UserHandle parentUser;
+
+        UserInfo parentUserInfo = mUserManager.getProfileParent(user.getIdentifier());
+        if (parentUserInfo != null) {
+            parentUser = parentUserInfo.getUserHandle();
+        } else {
+            parentUser = user;
+        }
+
+        synchronized (mSettingsByProfileGroup) {
+            UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.get(
+                    parentUser.getIdentifier());
+            if (settings == null) {
+                settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this);
+                mSettingsByProfileGroup.put(parentUser.getIdentifier(), settings);
+            }
+            return settings;
+        }
+    }
+
+    /**
      * Remove the settings for a user.
      *
      * @param userIdToRemove The user o remove
@@ -83,7 +123,8 @@
      */
     void dump(@NonNull IndentingPrintWriter pw) {
         synchronized (mSettingsByUser) {
-            for (int i = 0; i < mSettingsByUser.size(); i++) {
+            int numUsers = mSettingsByUser.size();
+            for (int i = 0; i < numUsers; i++) {
                 final int userId = mSettingsByUser.keyAt(i);
                 final UsbUserSettingsManager settings = mSettingsByUser.valueAt(i);
                 pw.println("Settings for user " + userId + ":");
@@ -95,6 +136,21 @@
                 }
             }
         }
+
+        synchronized (mSettingsByProfileGroup) {
+            int numProfileGroups = mSettingsByProfileGroup.size();
+            for (int i = 0; i < numProfileGroups; i++) {
+                final int parentUserId = mSettingsByProfileGroup.keyAt(i);
+                final UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.valueAt(i);
+                pw.println("Settings for profile group " + parentUserId + ":");
+                pw.increaseIndent();
+                try {
+                    settings.dump(pw);
+                } finally {
+                    pw.decreaseIndent();
+                }
+            }
+        }
     }
 
     /**
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 5fd265a..4a34ece 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -19,64 +19,30 @@
 import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.res.XmlResourceParser;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.Process;
 import android.os.UserHandle;
-import android.util.AtomicFile;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
-import android.util.Xml;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.XmlUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
-
-import libcore.io.IoUtils;
 
 class UsbUserSettingsManager {
     private static final String TAG = "UsbUserSettingsManager";
     private static final boolean DEBUG = false;
 
-    /** Legacy settings file, before multi-user */
-    private static final File sSingleUserSettingsFile = new File(
-            "/data/system/usb_device_manager.xml");
-
     private final UserHandle mUser;
-    private final AtomicFile mSettingsFile;
     private final boolean mDisablePermissionDialogs;
 
-    private final Context mContext;
     private final Context mUserContext;
     private final PackageManager mPackageManager;
 
@@ -86,417 +52,9 @@
     // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
     private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
             new HashMap<UsbAccessory, SparseBooleanArray>();
-    // Maps DeviceFilter to user preferred application package
-    private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
-            new HashMap<DeviceFilter, String>();
-    // Maps AccessoryFilter to user preferred application package
-    private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
-            new HashMap<AccessoryFilter, String>();
 
     private final Object mLock = new Object();
 
-    // This class is used to describe a USB device.
-    // When used in HashMaps all values must be specified,
-    // but wildcards can be used for any of the fields in
-    // the package meta-data.
-    private static class DeviceFilter {
-        // USB Vendor ID (or -1 for unspecified)
-        public final int mVendorId;
-        // USB Product ID (or -1 for unspecified)
-        public final int mProductId;
-        // USB device or interface class (or -1 for unspecified)
-        public final int mClass;
-        // USB device subclass (or -1 for unspecified)
-        public final int mSubclass;
-        // USB device protocol (or -1 for unspecified)
-        public final int mProtocol;
-        // USB device manufacturer name string (or null for unspecified)
-        public final String mManufacturerName;
-        // USB device product name string (or null for unspecified)
-        public final String mProductName;
-        // USB device serial number string (or null for unspecified)
-        public final String mSerialNumber;
-
-        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
-                            String manufacturer, String product, String serialnum) {
-            mVendorId = vid;
-            mProductId = pid;
-            mClass = clasz;
-            mSubclass = subclass;
-            mProtocol = protocol;
-            mManufacturerName = manufacturer;
-            mProductName = product;
-            mSerialNumber = serialnum;
-        }
-
-        public DeviceFilter(UsbDevice device) {
-            mVendorId = device.getVendorId();
-            mProductId = device.getProductId();
-            mClass = device.getDeviceClass();
-            mSubclass = device.getDeviceSubclass();
-            mProtocol = device.getDeviceProtocol();
-            mManufacturerName = device.getManufacturerName();
-            mProductName = device.getProductName();
-            mSerialNumber = device.getSerialNumber();
-        }
-
-        public static DeviceFilter read(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            int vendorId = -1;
-            int productId = -1;
-            int deviceClass = -1;
-            int deviceSubclass = -1;
-            int deviceProtocol = -1;
-            String manufacturerName = null;
-            String productName = null;
-            String serialNumber = null;
-
-            int count = parser.getAttributeCount();
-            for (int i = 0; i < count; i++) {
-                String name = parser.getAttributeName(i);
-                String value = parser.getAttributeValue(i);
-                // Attribute values are ints or strings
-                if ("manufacturer-name".equals(name)) {
-                    manufacturerName = value;
-                } else if ("product-name".equals(name)) {
-                    productName = value;
-                } else if ("serial-number".equals(name)) {
-                    serialNumber = value;
-                } else {
-                    int intValue = -1;
-                    int radix = 10;
-                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
-                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
-                        // allow hex values starting with 0x or 0X
-                        radix = 16;
-                        value = value.substring(2);
-                    }
-                    try {
-                        intValue = Integer.parseInt(value, radix);
-                    } catch (NumberFormatException e) {
-                        Slog.e(TAG, "invalid number for field " + name, e);
-                        continue;
-                    }
-                    if ("vendor-id".equals(name)) {
-                        vendorId = intValue;
-                    } else if ("product-id".equals(name)) {
-                        productId = intValue;
-                    } else if ("class".equals(name)) {
-                        deviceClass = intValue;
-                    } else if ("subclass".equals(name)) {
-                        deviceSubclass = intValue;
-                    } else if ("protocol".equals(name)) {
-                        deviceProtocol = intValue;
-                    }
-                }
-            }
-            return new DeviceFilter(vendorId, productId,
-                    deviceClass, deviceSubclass, deviceProtocol,
-                    manufacturerName, productName, serialNumber);
-        }
-
-        public void write(XmlSerializer serializer) throws IOException {
-            serializer.startTag(null, "usb-device");
-            if (mVendorId != -1) {
-                serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
-            }
-            if (mProductId != -1) {
-                serializer.attribute(null, "product-id", Integer.toString(mProductId));
-            }
-            if (mClass != -1) {
-                serializer.attribute(null, "class", Integer.toString(mClass));
-            }
-            if (mSubclass != -1) {
-                serializer.attribute(null, "subclass", Integer.toString(mSubclass));
-            }
-            if (mProtocol != -1) {
-                serializer.attribute(null, "protocol", Integer.toString(mProtocol));
-            }
-            if (mManufacturerName != null) {
-                serializer.attribute(null, "manufacturer-name", mManufacturerName);
-            }
-            if (mProductName != null) {
-                serializer.attribute(null, "product-name", mProductName);
-            }
-            if (mSerialNumber != null) {
-                serializer.attribute(null, "serial-number", mSerialNumber);
-            }
-            serializer.endTag(null, "usb-device");
-        }
-
-        private boolean matches(int clasz, int subclass, int protocol) {
-            return ((mClass == -1 || clasz == mClass) &&
-                    (mSubclass == -1 || subclass == mSubclass) &&
-                    (mProtocol == -1 || protocol == mProtocol));
-        }
-
-        public boolean matches(UsbDevice device) {
-            if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
-            if (mProductId != -1 && device.getProductId() != mProductId) return false;
-            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
-            if (mProductName != null && device.getProductName() == null) return false;
-            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
-            if (mManufacturerName != null && device.getManufacturerName() != null &&
-                !mManufacturerName.equals(device.getManufacturerName())) return false;
-            if (mProductName != null && device.getProductName() != null &&
-                !mProductName.equals(device.getProductName())) return false;
-            if (mSerialNumber != null && device.getSerialNumber() != null &&
-                !mSerialNumber.equals(device.getSerialNumber())) return false;
-
-            // check device class/subclass/protocol
-            if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
-                    device.getDeviceProtocol())) return true;
-
-            // if device doesn't match, check the interfaces
-            int count = device.getInterfaceCount();
-            for (int i = 0; i < count; i++) {
-                UsbInterface intf = device.getInterface(i);
-                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
-                        intf.getInterfaceProtocol())) return true;
-            }
-
-            return false;
-        }
-
-        public boolean matches(DeviceFilter f) {
-            if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
-            if (mProductId != -1 && f.mProductId != mProductId) return false;
-            if (f.mManufacturerName != null && mManufacturerName == null) return false;
-            if (f.mProductName != null && mProductName == null) return false;
-            if (f.mSerialNumber != null && mSerialNumber == null) return false;
-            if (mManufacturerName != null && f.mManufacturerName != null &&
-                !mManufacturerName.equals(f.mManufacturerName)) return false;
-            if (mProductName != null && f.mProductName != null &&
-                !mProductName.equals(f.mProductName)) return false;
-            if (mSerialNumber != null && f.mSerialNumber != null &&
-                !mSerialNumber.equals(f.mSerialNumber)) return false;
-
-            // check device class/subclass/protocol
-            return matches(f.mClass, f.mSubclass, f.mProtocol);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // can't compare if we have wildcard strings
-            if (mVendorId == -1 || mProductId == -1 ||
-                    mClass == -1 || mSubclass == -1 || mProtocol == -1) {
-                return false;
-            }
-            if (obj instanceof DeviceFilter) {
-                DeviceFilter filter = (DeviceFilter)obj;
-
-                if (filter.mVendorId != mVendorId ||
-                        filter.mProductId != mProductId ||
-                        filter.mClass != mClass ||
-                        filter.mSubclass != mSubclass ||
-                        filter.mProtocol != mProtocol) {
-                    return(false);
-                }
-                if ((filter.mManufacturerName != null &&
-                        mManufacturerName == null) ||
-                    (filter.mManufacturerName == null &&
-                        mManufacturerName != null) ||
-                    (filter.mProductName != null &&
-                        mProductName == null)  ||
-                    (filter.mProductName == null &&
-                        mProductName != null) ||
-                    (filter.mSerialNumber != null &&
-                        mSerialNumber == null)  ||
-                    (filter.mSerialNumber == null &&
-                        mSerialNumber != null)) {
-                    return(false);
-                }
-                if  ((filter.mManufacturerName != null &&
-                        mManufacturerName != null &&
-                        !mManufacturerName.equals(filter.mManufacturerName)) ||
-                     (filter.mProductName != null &&
-                        mProductName != null &&
-                        !mProductName.equals(filter.mProductName)) ||
-                     (filter.mSerialNumber != null &&
-                        mSerialNumber != null &&
-                        !mSerialNumber.equals(filter.mSerialNumber))) {
-                    return(false);
-                }
-                return(true);
-            }
-            if (obj instanceof UsbDevice) {
-                UsbDevice device = (UsbDevice)obj;
-                if (device.getVendorId() != mVendorId ||
-                        device.getProductId() != mProductId ||
-                        device.getDeviceClass() != mClass ||
-                        device.getDeviceSubclass() != mSubclass ||
-                        device.getDeviceProtocol() != mProtocol) {
-                    return(false);
-                }
-                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
-                        (mManufacturerName == null && device.getManufacturerName() != null) ||
-                        (mProductName != null && device.getProductName() == null) ||
-                        (mProductName == null && device.getProductName() != null) ||
-                        (mSerialNumber != null && device.getSerialNumber() == null) ||
-                        (mSerialNumber == null && device.getSerialNumber() != null)) {
-                    return(false);
-                }
-                if ((device.getManufacturerName() != null &&
-                        !mManufacturerName.equals(device.getManufacturerName())) ||
-                        (device.getProductName() != null &&
-                            !mProductName.equals(device.getProductName())) ||
-                        (device.getSerialNumber() != null &&
-                            !mSerialNumber.equals(device.getSerialNumber()))) {
-                    return(false);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return (((mVendorId << 16) | mProductId) ^
-                    ((mClass << 16) | (mSubclass << 8) | mProtocol));
-        }
-
-        @Override
-        public String toString() {
-            return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
-                    ",mClass=" + mClass + ",mSubclass=" + mSubclass +
-                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
-                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
-                    "]";
-        }
-    }
-
-    // This class is used to describe a USB accessory.
-    // When used in HashMaps all values must be specified,
-    // but wildcards can be used for any of the fields in
-    // the package meta-data.
-    private static class AccessoryFilter {
-        // USB accessory manufacturer (or null for unspecified)
-        public final String mManufacturer;
-        // USB accessory model (or null for unspecified)
-        public final String mModel;
-        // USB accessory version (or null for unspecified)
-        public final String mVersion;
-
-        public AccessoryFilter(String manufacturer, String model, String version) {
-            mManufacturer = manufacturer;
-            mModel = model;
-            mVersion = version;
-        }
-
-        public AccessoryFilter(UsbAccessory accessory) {
-            mManufacturer = accessory.getManufacturer();
-            mModel = accessory.getModel();
-            mVersion = accessory.getVersion();
-        }
-
-        public static AccessoryFilter read(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            String manufacturer = null;
-            String model = null;
-            String version = null;
-
-            int count = parser.getAttributeCount();
-            for (int i = 0; i < count; i++) {
-                String name = parser.getAttributeName(i);
-                String value = parser.getAttributeValue(i);
-
-                if ("manufacturer".equals(name)) {
-                    manufacturer = value;
-                } else if ("model".equals(name)) {
-                    model = value;
-                } else if ("version".equals(name)) {
-                    version = value;
-                }
-             }
-             return new AccessoryFilter(manufacturer, model, version);
-        }
-
-        public void write(XmlSerializer serializer)throws IOException {
-            serializer.startTag(null, "usb-accessory");
-            if (mManufacturer != null) {
-                serializer.attribute(null, "manufacturer", mManufacturer);
-            }
-            if (mModel != null) {
-                serializer.attribute(null, "model", mModel);
-            }
-            if (mVersion != null) {
-                serializer.attribute(null, "version", mVersion);
-            }
-            serializer.endTag(null, "usb-accessory");
-        }
-
-        public boolean matches(UsbAccessory acc) {
-            if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
-            if (mModel != null && !acc.getModel().equals(mModel)) return false;
-            if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
-            return true;
-        }
-
-        public boolean matches(AccessoryFilter f) {
-            if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
-            if (mModel != null && !f.mModel.equals(mModel)) return false;
-            if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
-            return true;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // can't compare if we have wildcard strings
-            if (mManufacturer == null || mModel == null || mVersion == null) {
-                return false;
-            }
-            if (obj instanceof AccessoryFilter) {
-                AccessoryFilter filter = (AccessoryFilter)obj;
-                return (mManufacturer.equals(filter.mManufacturer) &&
-                        mModel.equals(filter.mModel) &&
-                        mVersion.equals(filter.mVersion));
-            }
-            if (obj instanceof UsbAccessory) {
-                UsbAccessory accessory = (UsbAccessory)obj;
-                return (mManufacturer.equals(accessory.getManufacturer()) &&
-                        mModel.equals(accessory.getModel()) &&
-                        mVersion.equals(accessory.getVersion()));
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
-                    (mModel == null ? 0 : mModel.hashCode()) ^
-                    (mVersion == null ? 0 : mVersion.hashCode()));
-        }
-
-        @Override
-        public String toString() {
-            return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
-                                "\", mModel=\"" + mModel +
-                                "\", mVersion=\"" + mVersion + "\"]";
-        }
-    }
-
-    private class MyPackageMonitor extends PackageMonitor {
-        @Override
-        public void onPackageAdded(String packageName, int uid) {
-            handlePackageUpdate(packageName);
-        }
-
-        @Override
-        public boolean onPackageChanged(String packageName, int uid, String[] components) {
-            handlePackageUpdate(packageName);
-            return false;
-        }
-
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            clearDefaults(packageName);
-        }
-    }
-
-    MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
-
-    private final MtpNotificationManager mMtpNotificationManager;
-
     public UsbUserSettingsManager(Context context, UserHandle user) {
         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
 
@@ -506,284 +64,12 @@
             throw new RuntimeException("Missing android package");
         }
 
-        mContext = context;
         mPackageManager = mUserContext.getPackageManager();
 
         mUser = user;
-        mSettingsFile = new AtomicFile(new File(
-                Environment.getUserSystemDirectory(user.getIdentifier()),
-                "usb_device_manager.xml"));
 
         mDisablePermissionDialogs = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
-
-        synchronized (mLock) {
-            if (UserHandle.SYSTEM.equals(user)) {
-                upgradeSingleUserLocked();
-            }
-            readSettingsLocked();
-        }
-
-        mPackageMonitor.register(mUserContext, null, true);
-        mMtpNotificationManager = new MtpNotificationManager(
-                context,
-                new MtpNotificationManager.OnOpenInAppListener() {
-                    @Override
-                    public void onOpenInApp(UsbDevice device) {
-                        resolveActivity(createDeviceAttachedIntent(device), device);
-                    }
-                });
-    }
-
-    private void readPreference(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        String packageName = null;
-        int count = parser.getAttributeCount();
-        for (int i = 0; i < count; i++) {
-            if ("package".equals(parser.getAttributeName(i))) {
-                packageName = parser.getAttributeValue(i);
-                break;
-            }
-        }
-        XmlUtils.nextElement(parser);
-        if ("usb-device".equals(parser.getName())) {
-            DeviceFilter filter = DeviceFilter.read(parser);
-            mDevicePreferenceMap.put(filter, packageName);
-        } else if ("usb-accessory".equals(parser.getName())) {
-            AccessoryFilter filter = AccessoryFilter.read(parser);
-            mAccessoryPreferenceMap.put(filter, packageName);
-        }
-        XmlUtils.nextElement(parser);
-    }
-
-    /**
-     * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
-     * Should only by called by owner.
-     */
-    private void upgradeSingleUserLocked() {
-        if (sSingleUserSettingsFile.exists()) {
-            mDevicePreferenceMap.clear();
-            mAccessoryPreferenceMap.clear();
-
-            FileInputStream fis = null;
-            try {
-                fis = new FileInputStream(sSingleUserSettingsFile);
-                XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(fis, StandardCharsets.UTF_8.name());
-
-                XmlUtils.nextElement(parser);
-                while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                    final String tagName = parser.getName();
-                    if ("preference".equals(tagName)) {
-                        readPreference(parser);
-                    } else {
-                        XmlUtils.nextElement(parser);
-                    }
-                }
-            } catch (IOException e) {
-                Log.wtf(TAG, "Failed to read single-user settings", e);
-            } catch (XmlPullParserException e) {
-                Log.wtf(TAG, "Failed to read single-user settings", e);
-            } finally {
-                IoUtils.closeQuietly(fis);
-            }
-
-            writeSettingsLocked();
-
-            // Success or failure, we delete single-user file
-            sSingleUserSettingsFile.delete();
-        }
-    }
-
-    private void readSettingsLocked() {
-        if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
-
-        mDevicePreferenceMap.clear();
-        mAccessoryPreferenceMap.clear();
-
-        FileInputStream stream = null;
-        try {
-            stream = mSettingsFile.openRead();
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(stream, StandardCharsets.UTF_8.name());
-
-            XmlUtils.nextElement(parser);
-            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                String tagName = parser.getName();
-                if ("preference".equals(tagName)) {
-                    readPreference(parser);
-                } else {
-                    XmlUtils.nextElement(parser);
-                }
-            }
-        } catch (FileNotFoundException e) {
-            if (DEBUG) Slog.d(TAG, "settings file not found");
-        } catch (Exception e) {
-            Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
-            mSettingsFile.delete();
-        } finally {
-            IoUtils.closeQuietly(stream);
-        }
-    }
-
-    private void writeSettingsLocked() {
-        if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
-
-        FileOutputStream fos = null;
-        try {
-            fos = mSettingsFile.startWrite();
-
-            FastXmlSerializer serializer = new FastXmlSerializer();
-            serializer.setOutput(fos, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            serializer.startTag(null, "settings");
-
-            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
-                serializer.startTag(null, "preference");
-                serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
-                filter.write(serializer);
-                serializer.endTag(null, "preference");
-            }
-
-            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
-                serializer.startTag(null, "preference");
-                serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
-                filter.write(serializer);
-                serializer.endTag(null, "preference");
-            }
-
-            serializer.endTag(null, "settings");
-            serializer.endDocument();
-
-            mSettingsFile.finishWrite(fos);
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to write settings", e);
-            if (fos != null) {
-                mSettingsFile.failWrite(fos);
-            }
-        }
-    }
-
-    // Checks to see if a package matches a device or accessory.
-    // Only one of device and accessory should be non-null.
-    private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
-            UsbDevice device, UsbAccessory accessory) {
-        ActivityInfo ai = info.activityInfo;
-
-        XmlResourceParser parser = null;
-        try {
-            parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
-            if (parser == null) {
-                Slog.w(TAG, "no meta-data for " + info);
-                return false;
-            }
-
-            XmlUtils.nextElement(parser);
-            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                String tagName = parser.getName();
-                if (device != null && "usb-device".equals(tagName)) {
-                    DeviceFilter filter = DeviceFilter.read(parser);
-                    if (filter.matches(device)) {
-                        return true;
-                    }
-                }
-                else if (accessory != null && "usb-accessory".equals(tagName)) {
-                    AccessoryFilter filter = AccessoryFilter.read(parser);
-                    if (filter.matches(accessory)) {
-                        return true;
-                    }
-                }
-                XmlUtils.nextElement(parser);
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to load component info " + info.toString(), e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return false;
-    }
-
-    private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
-        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
-        List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
-                PackageManager.GET_META_DATA);
-        int count = resolveInfos.size();
-        for (int i = 0; i < count; i++) {
-            ResolveInfo resolveInfo = resolveInfos.get(i);
-            if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
-                matches.add(resolveInfo);
-            }
-        }
-        return matches;
-    }
-
-    private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
-            UsbAccessory accessory, Intent intent) {
-        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
-        List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
-                PackageManager.GET_META_DATA);
-        int count = resolveInfos.size();
-        for (int i = 0; i < count; i++) {
-            ResolveInfo resolveInfo = resolveInfos.get(i);
-            if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
-                matches.add(resolveInfo);
-            }
-        }
-        return matches;
-    }
-
-    public void deviceAttached(UsbDevice device) {
-        final Intent intent = createDeviceAttachedIntent(device);
-
-        // Send broadcast to running activities with registered intent
-        mUserContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-
-        if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
-            // Show notification if the device is MTP storage.
-            mMtpNotificationManager.showNotification(device);
-        } else {
-            resolveActivity(intent, device);
-        }
-    }
-
-    private void resolveActivity(Intent intent, UsbDevice device) {
-        ArrayList<ResolveInfo> matches;
-        String defaultPackage;
-        synchronized (mLock) {
-            matches = getDeviceMatchesLocked(device, intent);
-            // Launch our default activity directly, if we have one.
-            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
-            defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
-        }
-
-        // Start activity with registered intent
-        resolveActivity(intent, matches, defaultPackage, device, null);
-    }
-
-    public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
-        final Intent intent = createDeviceAttachedIntent(device);
-
-        // Send broadcast to running activity with registered intent
-        mUserContext.sendBroadcast(intent);
-
-        ApplicationInfo appInfo;
-        try {
-            appInfo = mPackageManager.getApplicationInfo(component.getPackageName(), 0);
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Default USB handling package not found: " + component.getPackageName());
-            return;
-        }
-
-        grantDevicePermission(device, appInfo.uid);
-
-        Intent activityIntent = new Intent(intent);
-        activityIntent.setComponent(component);
-        try {
-            mUserContext.startActivityAsUser(activityIntent, mUser);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(TAG, "unable to start activity " + activityIntent);
-        }
     }
 
     /**
@@ -798,32 +84,6 @@
     }
 
     /**
-     * Remove notifications for a usb device.
-     *
-     * @param device The device the notifications are for.
-     */
-    void usbDeviceRemoved(@NonNull UsbDevice device) {
-        mMtpNotificationManager.hideNotification(device.getDeviceId());
-    }
-
-    public void accessoryAttached(UsbAccessory accessory) {
-        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
-        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        ArrayList<ResolveInfo> matches;
-        String defaultPackage;
-        synchronized (mLock) {
-            matches = getAccessoryMatchesLocked(accessory, intent);
-            // Launch our default activity directly, if we have one.
-            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
-            defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
-        }
-
-        resolveActivity(intent, matches, defaultPackage, null, accessory);
-    }
-
-    /**
      * Remove all access permission for a accessory.
      *
      * @param accessory The accessory the permissions are for
@@ -834,204 +94,6 @@
         }
     }
 
-    private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
-            String defaultPackage, UsbDevice device, UsbAccessory accessory) {
-        int count = matches.size();
-
-        // don't show the resolver activity if there are no choices available
-        if (count == 0) {
-            if (accessory != null) {
-                String uri = accessory.getUri();
-                if (uri != null && uri.length() > 0) {
-                    // display URI to user
-                    // start UsbResolverActivity so user can choose an activity
-                    Intent dialogIntent = new Intent();
-                    dialogIntent.setClassName("com.android.systemui",
-                            "com.android.systemui.usb.UsbAccessoryUriActivity");
-                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                    dialogIntent.putExtra("uri", uri);
-                    try {
-                        mUserContext.startActivityAsUser(dialogIntent, mUser);
-                    } catch (ActivityNotFoundException e) {
-                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
-                    }
-                }
-            }
-
-            // do nothing
-            return;
-        }
-
-        ResolveInfo defaultRI = null;
-        if (count == 1 && defaultPackage == null) {
-            // Check to see if our single choice is on the system partition.
-            // If so, treat it as our default without calling UsbResolverActivity
-            ResolveInfo rInfo = matches.get(0);
-            if (rInfo.activityInfo != null &&
-                    rInfo.activityInfo.applicationInfo != null &&
-                    (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                defaultRI = rInfo;
-            }
-
-            if (mDisablePermissionDialogs) {
-                // bypass dialog and launch the only matching activity
-                rInfo = matches.get(0);
-                if (rInfo.activityInfo != null) {
-                    defaultPackage = rInfo.activityInfo.packageName;
-                }
-            }
-        }
-
-        if (defaultRI == null && defaultPackage != null) {
-            // look for default activity
-            for (int i = 0; i < count; i++) {
-                ResolveInfo rInfo = matches.get(i);
-                if (rInfo.activityInfo != null &&
-                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
-                    defaultRI = rInfo;
-                    break;
-                }
-            }
-        }
-
-        if (defaultRI != null) {
-            // grant permission for default activity
-            if (device != null) {
-                grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
-            } else if (accessory != null) {
-                grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
-            }
-
-            // start default activity directly
-            try {
-                intent.setComponent(
-                        new ComponentName(defaultRI.activityInfo.packageName,
-                                defaultRI.activityInfo.name));
-                mUserContext.startActivityAsUser(intent, mUser);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "startActivity failed", e);
-            }
-        } else {
-            Intent resolverIntent = new Intent();
-            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            if (count == 1) {
-                // start UsbConfirmActivity if there is only one choice
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbConfirmActivity");
-                resolverIntent.putExtra("rinfo", matches.get(0));
-
-                if (device != null) {
-                    resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
-                } else {
-                    resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                }
-            } else {
-                // start UsbResolverActivity so user can choose an activity
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbResolverActivity");
-                resolverIntent.putParcelableArrayListExtra("rlist", matches);
-                resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
-            }
-            try {
-                mUserContext.startActivityAsUser(resolverIntent, mUser);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "unable to start activity " + resolverIntent);
-            }
-        }
-    }
-
-    private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
-        boolean changed = false;
-        for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
-            if (filter.matches(test)) {
-                mDevicePreferenceMap.remove(test);
-                changed = true;
-            }
-        }
-        return changed;
-    }
-
-    private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
-        boolean changed = false;
-        for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
-            if (filter.matches(test)) {
-                mAccessoryPreferenceMap.remove(test);
-                changed = true;
-            }
-        }
-        return changed;
-    }
-
-    private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
-            String metaDataName) {
-        XmlResourceParser parser = null;
-        boolean changed = false;
-
-        try {
-            parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
-            if (parser == null) return false;
-
-            XmlUtils.nextElement(parser);
-            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                String tagName = parser.getName();
-                if ("usb-device".equals(tagName)) {
-                    DeviceFilter filter = DeviceFilter.read(parser);
-                    if (clearCompatibleMatchesLocked(packageName, filter)) {
-                        changed = true;
-                    }
-                }
-                else if ("usb-accessory".equals(tagName)) {
-                    AccessoryFilter filter = AccessoryFilter.read(parser);
-                    if (clearCompatibleMatchesLocked(packageName, filter)) {
-                        changed = true;
-                    }
-                }
-                XmlUtils.nextElement(parser);
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return changed;
-    }
-
-    // Check to see if the package supports any USB devices or accessories.
-    // If so, clear any non-matching preferences for matching devices/accessories.
-    private void handlePackageUpdate(String packageName) {
-        synchronized (mLock) {
-            PackageInfo info;
-            boolean changed = false;
-
-            try {
-                info = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
-            } catch (NameNotFoundException e) {
-                Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
-                return;
-            }
-
-            ActivityInfo[] activities = info.activities;
-            if (activities == null) return;
-            for (int i = 0; i < activities.length; i++) {
-                // check for meta-data, both for devices and accessories
-                if (handlePackageUpdateLocked(packageName, activities[i],
-                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
-                    changed = true;
-                }
-                if (handlePackageUpdateLocked(packageName, activities[i],
-                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
-                    changed = true;
-                }
-            }
-
-            if (changed) {
-                writeSettingsLocked();
-            }
-        }
-    }
 
     public boolean hasPermission(UsbDevice device) {
         synchronized (mLock) {
@@ -1142,42 +204,6 @@
         requestPermissionDialog(intent, packageName, pi);
     }
 
-    public void setDevicePackage(UsbDevice device, String packageName) {
-        DeviceFilter filter = new DeviceFilter(device);
-        boolean changed = false;
-        synchronized (mLock) {
-            if (packageName == null) {
-                changed = (mDevicePreferenceMap.remove(filter) != null);
-            } else {
-                changed = !packageName.equals(mDevicePreferenceMap.get(filter));
-                if (changed) {
-                    mDevicePreferenceMap.put(filter, packageName);
-                }
-            }
-            if (changed) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
-    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
-        AccessoryFilter filter = new AccessoryFilter(accessory);
-        boolean changed = false;
-        synchronized (mLock) {
-            if (packageName == null) {
-                changed = (mAccessoryPreferenceMap.remove(filter) != null);
-            } else {
-                changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
-                if (changed) {
-                    mAccessoryPreferenceMap.put(filter, packageName);
-                }
-            }
-            if (changed) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
     public void grantDevicePermission(UsbDevice device, int uid) {
         synchronized (mLock) {
             String deviceName = device.getDeviceName();
@@ -1201,51 +227,6 @@
         }
     }
 
-    public boolean hasDefaults(String packageName) {
-        synchronized (mLock) {
-            if (mDevicePreferenceMap.values().contains(packageName)) return true;
-            if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
-            return false;
-        }
-    }
-
-    public void clearDefaults(String packageName) {
-        synchronized (mLock) {
-            if (clearPackageDefaultsLocked(packageName)) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
-    private boolean clearPackageDefaultsLocked(String packageName) {
-        boolean cleared = false;
-        synchronized (mLock) {
-            if (mDevicePreferenceMap.containsValue(packageName)) {
-                // make a copy of the key set to avoid ConcurrentModificationException
-                Object[] keys = mDevicePreferenceMap.keySet().toArray();
-                for (int i = 0; i < keys.length; i++) {
-                    Object key = keys[i];
-                    if (packageName.equals(mDevicePreferenceMap.get(key))) {
-                        mDevicePreferenceMap.remove(key);
-                        cleared = true;
-                    }
-                }
-            }
-            if (mAccessoryPreferenceMap.containsValue(packageName)) {
-                // make a copy of the key set to avoid ConcurrentModificationException
-                Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
-                for (int i = 0; i < keys.length; i++) {
-                    Object key = keys[i];
-                    if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
-                        mAccessoryPreferenceMap.remove(key);
-                        cleared = true;
-                    }
-                }
-            }
-            return cleared;
-        }
-    }
-
     public void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
             pw.println("Device permissions:");
@@ -1268,21 +249,6 @@
                 }
                 pw.println();
             }
-            pw.println("Device preferences:");
-            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
-                pw.println("  " + filter + ": " + mDevicePreferenceMap.get(filter));
-            }
-            pw.println("Accessory preferences:");
-            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
-                pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
-            }
         }
     }
-
-    private static Intent createDeviceAttachedIntent(UsbDevice device) {
-        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
-        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return intent;
-    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 0c75630..7b68a4c 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1082,7 +1082,7 @@
      *
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
-     * @param request Details about the incoming call.
+     * @param request Details about the outgoing call.
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 871aae8..bdc9c45 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -87,6 +87,14 @@
             KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
 
     /**
+     * Flag indicating whether radio is to be restarted on error PDP_FAIL_REGULAR_DEACTIVATION
+     * This is false by default.
+     */
+    public static final String
+            KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL =
+                    "restart_radio_on_pdp_fail_regular_deactivation_bool";
+
+    /**
      * If true, enable vibration (haptic feedback) for key presses in the EmergencyDialer activity.
      * The pattern is set on a per-platform basis using config_virtualKeyVibePattern. To be
      * consistent with the regular Dialer, this value should agree with the corresponding values
@@ -269,6 +277,14 @@
             "support_downgrade_vt_to_audio_bool";
 
     /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number.
+     * When empty string, no default voicemail number is specified.
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+
+
+    /**
      * Flag specifying whether WFC over IMS should be available for carrier: independent of
      * carrier provisioning. If false: hard disabled. If true: then depends on carrier
      * provisioning, availability etc.
@@ -276,6 +292,16 @@
     public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
 
     /**
+     * Specifies a map from dialstrings to replacements for roaming network service numbers which
+     * cannot be replaced on the carrier side.
+     * <p>
+     * Individual entries have the format:
+     * [dialstring to replace]:[replacement]
+     */
+    public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY =
+            "dial_string_replace_string_array";
+
+    /**
      * Flag specifying whether WFC over IMS supports the "wifi only" option.  If false, the wifi
      * calling settings will not include an option for "wifi only".  If true, the wifi calling
      * settings will include an option for "wifi only"
@@ -433,18 +459,11 @@
             "disable_severe_when_extreme_disabled_bool";
 
     /**
-     * The data call APN retry configuration for default type APN.
+     * The data call retry configuration for different types of APN.
      * @hide
      */
-    public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_DEFAULT_STRING =
-            "carrier_data_call_retry_config_default_string";
-
-    /**
-     * The data call APN retry configuration for other type APNs.
-     * @hide
-     */
-    public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_OTHERS_STRING =
-            "carrier_data_call_retry_config_others_string";
+    public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS =
+            "carrier_data_call_retry_config_strings";
 
     /**
      * Delay between trying APN from the pool
@@ -683,6 +702,13 @@
     public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
 
     /**
+     * APN types that user is not allowed to modify
+     * @hide
+     */
+    public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY =
+            "read_only_apn_types_string_array";
+
+    /**
      * Boolean indicating if intent for emergency call state changes should be broadcast
      * @hide
      */
@@ -818,6 +844,14 @@
             "duration_blocking_disabled_after_emergency_int";
 
     /**
+     * For carriers which require an empty flash to be sent before sending the normal 3-way calling
+     * flash, the duration in milliseconds of the empty flash to send.  When {@code 0}, no empty
+     * flash is sent.
+     */
+    public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
+
+
+    /**
      * @hide
      * The default value for preferred CDMA roaming mode (aka CDMA system select.)
      *          CDMA_ROAMING_MODE_RADIO_DEFAULT = the default roaming mode from the radio
@@ -986,6 +1020,7 @@
         sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
         sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
@@ -1024,6 +1059,7 @@
         sDefaults.putBoolean(KEY_VOICE_PRIVACY_DISABLE_UI_BOOL, false);
         sDefaults.putBoolean(KEY_WORLD_PHONE_BOOL, false);
         sDefaults.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+        sDefaults.putBoolean(KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL, false);
         sDefaults.putInt(KEY_VOLTE_REPLACEMENT_RAT_INT, 0);
         sDefaults.putString(KEY_DEFAULT_SIM_CALL_MANAGER_STRING, "");
         sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
@@ -1039,14 +1075,16 @@
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
         sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
+        sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true);
-        sDefaults.putString(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_DEFAULT_STRING,
-                "default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
-                        + "320000:5000,640000:5000,1280000:5000,1800000:5000");
-        sDefaults.putString(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_OTHERS_STRING,
-                "max_retries=3, 5000, 5000, 5000");
+        sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
+                "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+                        + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+                "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+                        + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+                "others:max_retries=3, 5000, 5000, 5000"});
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
         sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
@@ -1060,10 +1098,12 @@
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
         sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
+        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_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 6b2ae3e..152b868 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -24,6 +24,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.location.CountryDetector;
 import android.net.Uri;
@@ -3021,4 +3022,79 @@
         return SubscriptionManager.getDefaultVoiceSubscriptionId();
     }
     //==== End of utility methods used only in compareStrictly() =====
+
+
+    /*
+     * The config held calling number conversion map, expected to convert to emergency number.
+     */
+    private static final String[] CONVERT_TO_EMERGENCY_MAP = Resources.getSystem().getStringArray(
+            com.android.internal.R.array.config_convert_to_emergency_number_map);
+    /**
+     * Check whether conversion to emergency number is enabled
+     *
+     * @return {@code true} when conversion to emergency numbers is enabled,
+     *         {@code false} otherwise
+     *
+     * @hide
+     */
+    public static boolean isConvertToEmergencyNumberEnabled() {
+        return CONVERT_TO_EMERGENCY_MAP != null && CONVERT_TO_EMERGENCY_MAP.length > 0;
+    }
+
+    /**
+     * Converts to emergency number based on the conversion map.
+     * The conversion map is declared as config_convert_to_emergency_number_map.
+     *
+     * Make sure {@link #isConvertToEmergencyNumberEnabled} is true before calling
+     * this function.
+     *
+     * @return The converted emergency number if the number matches conversion map,
+     * otherwise original number.
+     *
+     * @hide
+     */
+    public static String convertToEmergencyNumber(String number) {
+        if (TextUtils.isEmpty(number)) {
+            return number;
+        }
+
+        String normalizedNumber = normalizeNumber(number);
+
+        // The number is already emergency number. Skip conversion.
+        if (isEmergencyNumber(normalizedNumber)) {
+            return number;
+        }
+
+        for (String convertMap : CONVERT_TO_EMERGENCY_MAP) {
+            if (DBG) log("convertToEmergencyNumber: " + convertMap);
+            String[] entry = null;
+            String[] filterNumbers = null;
+            String convertedNumber = null;
+            if (!TextUtils.isEmpty(convertMap)) {
+                entry = convertMap.split(":");
+            }
+            if (entry != null && entry.length == 2) {
+                convertedNumber = entry[1];
+                if (!TextUtils.isEmpty(entry[0])) {
+                    filterNumbers = entry[0].split(",");
+                }
+            }
+            // Skip if the format of entry is invalid
+            if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
+                    || filterNumbers.length == 0) {
+                continue;
+            }
+
+            for (String filterNumber : filterNumbers) {
+                if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
+                        + ", convertedNumber = " + convertedNumber);
+                if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
+                    if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
+                            + convertedNumber);
+                    return convertedNumber;
+                }
+            }
+        }
+        return number;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index b417a1c..fdc68b9 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -140,6 +140,18 @@
     /** APN type for Emergency PDN. This is not an IA apn, but is used
      * for access to carrier services in an emergency call situation. */
     public static final String APN_TYPE_EMERGENCY = "emergency";
+    /** Array of all APN types */
+    public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
+            APN_TYPE_MMS,
+            APN_TYPE_SUPL,
+            APN_TYPE_DUN,
+            APN_TYPE_HIPRI,
+            APN_TYPE_FOTA,
+            APN_TYPE_IMS,
+            APN_TYPE_CBS,
+            APN_TYPE_IA,
+            APN_TYPE_EMERGENCY
+    };
 
     public static final int RIL_CARD_MAX_APPS    = 8;
 
diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
index 0111bc6..2ad0da9 100644
--- a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
+++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
@@ -15,6 +15,17 @@
         Decoder d = new Decoder(ByteBuffer.wrap(data));
 
         mViews = new ArrayList<>(100);
+
+        boolean dataIncludesWindowPosition = (data[0] == 'S');
+        Short windowLeftKey = null, windowTopKey = null;
+        Integer windowLeftValue = null, windowTopValue = null;
+        if (dataIncludesWindowPosition) {
+            windowLeftKey = (Short) d.readObject();
+            windowLeftValue = (Integer) d.readObject();
+            windowTopKey = (Short) d.readObject();
+            windowTopValue = (Integer) d.readObject();
+        }
+
         while (d.hasRemaining()) {
             Object o = d.readObject();
             if (o instanceof Map) {
@@ -27,6 +38,11 @@
             return;
         }
 
+        if (dataIncludesWindowPosition) {
+          mViews.get(0).put(windowLeftKey, windowLeftValue);
+          mViews.get(0).put(windowTopKey, windowTopValue);
+        }
+
         // the last one is the property map
         Map<Short,Object> idMap = mViews.remove(mViews.size() - 1);
         mIds = reverse(idMap);
diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml
index 12143b7..cb5f6c7 100644
--- a/tests/UiBench/AndroidManifest.xml
+++ b/tests/UiBench/AndroidManifest.xml
@@ -93,13 +93,21 @@
         </activity>
         <activity
             android:name=".TrivialRecyclerViewActivity"
-            android:label="General/Trivial Recycler ListView" >
+            android:label="General/Trivial RecyclerView" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.uibench.TEST" />
             </intent-filter>
         </activity>
         <activity
+            android:name=".SlowBindRecyclerViewActivity"
+            android:label="General/Slow Bind RecyclerView" >
+        <intent-filter>
+            <action android:name="android.intent.action.MAIN" />
+            <category android:name="com.android.test.uibench.TEST" />
+        </intent-filter>
+        </activity>
+        <activity
             android:name=".ActivityTransition"
             android:label="Transitions/Activity Transition" >
             <intent-filter>
diff --git a/tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java b/tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java
new file mode 100644
index 0000000..e32862f
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/SlowBindRecyclerViewActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.test.uibench;
+
+import android.content.Context;
+import android.os.Trace;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import com.android.test.uibench.recyclerview.RvBoxAdapter;
+import com.android.test.uibench.recyclerview.RvCompatListActivity;
+
+import java.util.concurrent.TimeUnit;
+
+public class SlowBindRecyclerViewActivity extends RvCompatListActivity {
+    /**
+     * Spin wait. Used instead of sleeping so a core is used up for the duration, and so
+     * traces/sampled profiling show the sections as expensive, and not just a scheduling mistake.
+     */
+    private static void spinWaitMs(long ms) {
+        long start = System.nanoTime();
+        while (System.nanoTime() - start < TimeUnit.MILLISECONDS.toNanos(ms));
+    }
+
+    @Override
+    protected RecyclerView.LayoutManager createLayoutManager(Context context) {
+        return new GridLayoutManager(context, 3);
+    }
+
+    @Override
+    protected RecyclerView.Adapter createAdapter() {
+        return new RvBoxAdapter(this, TextUtils.buildSimpleStringList()) {
+            @Override
+            public void onBindViewHolder(ViewHolder holder, int position) {
+                Trace.beginSection("bind item " + position);
+
+                spinWaitMs(3);
+                super.onBindViewHolder(holder, position);
+                Trace.endSection();
+            }
+        };
+    }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java
new file mode 100644
index 0000000..3440f19
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvBoxAdapter.java
@@ -0,0 +1,99 @@
+/*
+ * 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.test.uibench.recyclerview;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class RvBoxAdapter extends RecyclerView.Adapter<RvBoxAdapter.ViewHolder> {
+
+    private int mBackground;
+
+    private List<String> mValues;
+
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        public TextView mTextView;
+
+        public ViewHolder(TextView v) {
+            super(v);
+            mTextView = v;
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + " '" + mTextView.getText();
+        }
+    }
+
+    public RvBoxAdapter(Context context, String[] strings) {
+        TypedValue val = new TypedValue();
+        if (context.getTheme() != null) {
+            context.getTheme().resolveAttribute(
+                    android.R.attr.selectableItemBackground, val, true);
+        }
+        mBackground = val.resourceId;
+        mValues = new ArrayList<>();
+        Collections.addAll(mValues, strings);
+    }
+
+    @Override
+    public RvBoxAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        final ViewHolder h = new ViewHolder(new TextView(parent.getContext()));
+        h.mTextView.setMinimumHeight(128);
+        h.mTextView.setPadding(20, 0, 20, 0);
+        h.mTextView.setFocusable(true);
+        h.mTextView.setBackgroundResource(mBackground);
+        RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT);
+        lp.leftMargin = 10;
+        lp.rightMargin = 5;
+        lp.topMargin = 20;
+        lp.bottomMargin = 15;
+        h.mTextView.setLayoutParams(lp);
+        return h;
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.mTextView.setText(position + ":" + mValues.get(position));
+        holder.mTextView.setMinHeight((200 + mValues.get(position).length() * 10));
+        holder.mTextView.setBackgroundColor(getBackgroundColor(position));
+    }
+
+    private int getBackgroundColor(int position) {
+        switch (position % 4) {
+            case 0: return Color.LTGRAY;
+            case 1: return Color.RED;
+            case 2: return Color.DKGRAY;
+            case 3: return Color.BLUE;
+        }
+        return Color.TRANSPARENT;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
index e08dbc6..939b661 100644
--- a/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/recyclerview/RvCompatListActivity.java
@@ -26,7 +26,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
 
 import com.android.test.uibench.R;
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index e7edcc5..45f5acd 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -194,7 +194,10 @@
     manifestAction["uses-configuration"];
     manifestAction["uses-feature"];
     manifestAction["supports-screens"];
+
     manifestAction["compatible-screens"];
+    manifestAction["compatible-screens"]["screen"];
+
     manifestAction["supports-gl-texture"];
 
     // Application actions.
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index 372cb20..219fa2d 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -314,8 +314,11 @@
             continue
         # For later fonts, we only check them if they have a script
         # defined, since the defined script may get them to a higher
-        # score even if they appear after the emoji font.
-        if emoji_font_seen and not record.scripts:
+        # score even if they appear after the emoji font. However,
+        # we should skip checking the text symbols font, since
+        # symbol fonts should be able to override the emoji display
+        # style when 'Zsym' is explicitly specified by the user.
+        if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
             continue
 
         # Check default emoji-style characters
diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
index 94f3f54..85584d3 100644
--- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java
@@ -34,7 +34,7 @@
         Display display = wm.getDefaultDisplay();
         ViewRootImpl root = new ViewRootImpl(context, display);
         AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
-                display, root, new Handler(), null);
+                display, root, new Handler(), null, context);
         info.mHasWindowFocus = true;
         info.mWindowVisibility = View.VISIBLE;
         info.mInTouchMode = false; // this is so that we can display selections.
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6ca13d6..4a70060 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -30,6 +30,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.view.AppTransitionAnimationSpec;
@@ -610,4 +611,12 @@
     public Bitmap screenshotWallpaper() throws RemoteException {
         return null;
     }
+
+    @Override
+    public void enableSurfaceTrace(ParcelFileDescriptor fd) throws RemoteException {
+    }
+
+    @Override
+    public void disableSurfaceTrace() throws RemoteException {
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index a39eb4d..769ee33 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -620,7 +620,8 @@
             int y = 0;
             int width;
             int height;
-            Rectangle clipBounds = originalGraphics.getClipBounds();
+            Rectangle clipBounds = originalGraphics.getClip() != null ? originalGraphics
+                    .getClipBounds() : null;
             if (clipBounds != null) {
                 if (clipBounds.width == 0 || clipBounds.height == 0) {
                     // Clip is 0 so no need to paint anything.
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 716f1d3..06e1b68 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -167,18 +167,32 @@
      * scan configuration parameters to be sent to {@link #startBackgroundScan}
      */
     public static class ScanSettings implements Parcelable {
+        /**
+         * Hidden network to be scanned for.
+         * {@hide}
+         */
+        public static class HiddenNetwork {
+            /** SSID of the network */
+            public String ssid;
+
+            /**
+             * Default constructor for HiddenNetwork.
+             */
+            public HiddenNetwork(String ssid) {
+                this.ssid = ssid;
+            }
+        }
 
         /** one of the WIFI_BAND values */
         public int band;
         /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */
         public ChannelSpec[] channels;
         /**
-         * list of networkId's of hidden networks to scan for.
-         * These Id's should correspond to the wpa_supplicant's networkId's and will be used
-         * in connectivity scans using wpa_supplicant.
+         * list of hidden networks to scan for. Explicit probe requests are sent out for such
+         * networks during scan. Only valid for single scan requests.
          * {@hide}
          * */
-        public int[] hiddenNetworkIds;
+        public HiddenNetwork[] hiddenNetworks;
         /** period of background scan; in millisecond, 0 => single shot scan */
         public int periodInMs;
         /** must have a valid REPORT_EVENT value */
@@ -233,7 +247,14 @@
             } else {
                 dest.writeInt(0);
             }
-            dest.writeIntArray(hiddenNetworkIds);
+            if (hiddenNetworks != null) {
+                dest.writeInt(hiddenNetworks.length);
+                for (int i = 0; i < hiddenNetworks.length; i++) {
+                    dest.writeString(hiddenNetworks[i].ssid);
+                }
+            } else {
+                dest.writeInt(0);
+            }
         }
 
         /** Implement the Parcelable interface {@hide} */
@@ -258,7 +279,12 @@
                             spec.passive = in.readInt() == 1;
                             settings.channels[i] = spec;
                         }
-                        settings.hiddenNetworkIds = in.createIntArray();
+                        int numNetworks = in.readInt();
+                        settings.hiddenNetworks = new HiddenNetwork[numNetworks];
+                        for (int i = 0; i < numNetworks; i++) {
+                            String ssid = in.readString();
+                            settings.hiddenNetworks[i] = new HiddenNetwork(ssid);;
+                        }
                         return settings;
                     }
 
@@ -286,6 +312,12 @@
          * {@hide}
          */
         private int mBucketsScanned;
+        /**
+         * Indicates that the scan results received are as a result of a scan of all available
+         * channels. This should only be expected to function for single scans.
+         * {@hide}
+         */
+        private boolean mAllChannelsScanned;
         /** all scan results discovered in this scan, sorted by timestamp in ascending order */
         private ScanResult mResults[];
 
@@ -298,10 +330,12 @@
         }
 
         /** {@hide} */
-        public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) {
+        public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned,
+                ScanResult[] results) {
             mId = id;
             mFlags = flags;
             mBucketsScanned = bucketsScanned;
+            mAllChannelsScanned = allChannelsScanned;
             mResults = results;
         }
 
@@ -309,6 +343,7 @@
             mId = s.mId;
             mFlags = s.mFlags;
             mBucketsScanned = s.mBucketsScanned;
+            mAllChannelsScanned = s.mAllChannelsScanned;
             mResults = new ScanResult[s.mResults.length];
             for (int i = 0; i < s.mResults.length; i++) {
                 ScanResult result = s.mResults[i];
@@ -330,6 +365,11 @@
             return mBucketsScanned;
         }
 
+        /** {@hide} */
+        public boolean isAllChannelsScanned() {
+            return mAllChannelsScanned;
+        }
+
         public ScanResult[] getResults() {
             return mResults;
         }
@@ -345,6 +385,7 @@
                 dest.writeInt(mId);
                 dest.writeInt(mFlags);
                 dest.writeInt(mBucketsScanned);
+                dest.writeInt(mAllChannelsScanned ? 1 : 0);
                 dest.writeInt(mResults.length);
                 for (int i = 0; i < mResults.length; i++) {
                     ScanResult result = mResults[i];
@@ -362,12 +403,13 @@
                         int id = in.readInt();
                         int flags = in.readInt();
                         int bucketsScanned = in.readInt();
+                        boolean allChannelsScanned = in.readInt() != 0;
                         int n = in.readInt();
                         ScanResult results[] = new ScanResult[n];
                         for (int i = 0; i < n; i++) {
                             results[i] = ScanResult.CREATOR.createFromParcel(in);
                         }
-                        return new ScanData(id, flags, bucketsScanned, results);
+                        return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results);
                     }
 
                     public ScanData[] newArray(int size) {
@@ -520,10 +562,6 @@
 
             /** SSID of the network */
             public String ssid;
-            /** Network ID in wpa_supplicant */
-            public int networkId;
-            /** Assigned priority for the network */
-            public int priority;
             /** Bitmask of the FLAG_XXX */
             public byte flags;
             /** Bitmask of the ATUH_XXX */
@@ -580,8 +618,6 @@
                 dest.writeInt(networkList.length);
                 for (int i = 0; i < networkList.length; i++) {
                     dest.writeString(networkList[i].ssid);
-                    dest.writeInt(networkList[i].networkId);
-                    dest.writeInt(networkList[i].priority);
                     dest.writeByte(networkList[i].flags);
                     dest.writeByte(networkList[i].authBitField);
                 }
@@ -608,8 +644,6 @@
                         for (int i = 0; i < numNetworks; i++) {
                             String ssid = in.readString();
                             PnoNetwork network = new PnoNetwork(ssid);
-                            network.networkId = in.readInt();
-                            network.priority = in.readInt();
                             network.flags = in.readByte();
                             network.authBitField = in.readByte();
                             settings.networkList[i] = network;
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/nan/ConfigRequest.java
index 44544de..78e4052 100644
--- a/wifi/java/android/net/wifi/nan/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/nan/ConfigRequest.java
@@ -23,14 +23,14 @@
 /**
  * Defines a request object to configure a Wi-Fi NAN network. Built using
  * {@link ConfigRequest.Builder}. Configuration is requested using
- * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}.
+ * {@link WifiNanManager#connect(android.os.Handler, ConfigRequest, WifiNanEventCallback)}.
  * Note that the actual achieved configuration may be different from the
  * requested configuration - since different applications may request different
  * configurations.
  *
  * @hide PROPOSED_NAN_API
  */
-public class ConfigRequest implements Parcelable {
+public final class ConfigRequest implements Parcelable {
     /**
      * Lower range of possible cluster ID.
      *
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java
index 71f99d9..6203f95 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.java
+++ b/wifi/java/android/net/wifi/nan/PublishConfig.java
@@ -37,7 +37,7 @@
  *
  * @hide PROPOSED_NAN_API
  */
-public class PublishConfig implements Parcelable {
+public final class PublishConfig implements Parcelable {
     /** @hide */
     @IntDef({
             PUBLISH_TYPE_UNSOLICITED, PUBLISH_TYPE_SOLICITED })
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
index 7904875..5a2c762 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
@@ -37,7 +37,7 @@
  *
  * @hide PROPOSED_NAN_API
  */
-public class SubscribeConfig implements Parcelable {
+public final class SubscribeConfig implements Parcelable {
     /** @hide */
     @IntDef({
             SUBSCRIBE_TYPE_PASSIVE, SUBSCRIBE_TYPE_ACTIVE })
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java
index 6e714f1..029e36a 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java
@@ -23,7 +23,7 @@
 
 /**
  * Base class for NAN events callbacks. Should be extended by applications and set when calling
- * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}. These are callbacks
+ * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)}. These are callbacks
  * applying to the NAN connection as a whole - not to specific publish or subscribe sessions -
  * for that see {@link WifiNanSessionCallback}.
  *
@@ -46,7 +46,7 @@
 
     /**
      * Indicates that a {@link ConfigRequest} passed in
-     * {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}
+     * {@link WifiNanManager#connect(android.os.Handler, ConfigRequest, WifiNanEventCallback)}
      * couldn't be applied since other connections already exist with an incompatible
      * configurations. Failure reason flag for {@link WifiNanEventCallback#onConnectFail(int)}.
      */
@@ -60,7 +60,7 @@
 
     /**
      * Called when NAN connect operation
-     * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}
+     * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)}
      * is completed and that we can now start discovery sessions or connections.
      */
     public void onConnectSuccess() {
@@ -69,7 +69,7 @@
 
     /**
      * Called when NAN connect operation
-     * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)} failed.
+     * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)} failed.
      *
      * @param reason Failure reason code, see
      *            {@code WifiNanEventCallback.REASON_*}.
@@ -91,7 +91,7 @@
      * <p>
      *     This callback is only called if the NAN connection enables it using
      *     {@link ConfigRequest.Builder#setEnableIdentityChangeCallback(boolean)} in
-     *     {@link WifiNanManager#connect(android.os.Looper, ConfigRequest, WifiNanEventCallback)}
+     *     {@link WifiNanManager#connect(android.os.Handler, ConfigRequest, WifiNanEventCallback)}
      *     . It is disabled by default since it may result in additional wake-ups of the host -
      *     increasing power.
      *
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index 82d22bc..24ee640 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -60,7 +60,7 @@
  * The class provides access to:
  * <ul>
  * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to
- * {@link #connect(Looper, WifiNanEventCallback)}.
+ * {@link #connect(Handler, WifiNanEventCallback)}.
  * <li>Create discovery sessions (publish or subscribe sessions).
  * Refer to {@link #publish(PublishConfig, WifiNanSessionCallback)} and
  * {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)}.
@@ -77,7 +77,7 @@
  *     Note that this broadcast is not sticky - you should register for it and then check the
  *     above API to avoid a race condition.
  * <p>
- *     An application must use {@link #connect(Looper, WifiNanEventCallback)} to initialize a NAN
+ *     An application must use {@link #connect(Handler, WifiNanEventCallback)} to initialize a NAN
  *     cluster - before making any other NAN operation. NAN cluster membership is a device-wide
  *     operation - the API guarantees that the device is in a cluster or joins a NAN cluster (or
  *     starts one if none can be found). Information about connection success (or failure) are
@@ -343,13 +343,13 @@
      * Note: a NAN cluster is a shared resource - if the device is already connected to a cluster
      * than this function will simply indicate success immediately.
      *
-     * @param looper The Looper on which to execute all callbacks related to the
+     * @param handler The Handler on whose thread to execute all callbacks related to the
      *            connection - including all sessions opened as part of this
-     *            connection.
+     *            connection. If a null is provided then the application's main thread will be used.
      * @param callback A callback extended from {@link WifiNanEventCallback}.
      */
-    public void connect(@NonNull Looper looper, @NonNull WifiNanEventCallback callback) {
-        connect(looper, null, callback);
+    public void connect(@Nullable Handler handler, @NonNull WifiNanEventCallback callback) {
+        connect(handler, null, callback);
     }
 
     /**
@@ -360,30 +360,31 @@
      * An application <b>must</b> call {@link #disconnect()} when done with the Wi-Fi NAN
      * connection. Allows requesting a specific configuration using {@link ConfigRequest}. If not
      * necessary (default configuration should usually work) use the
-     * {@link #connect(Looper, WifiNanEventCallback)} method instead.
+     * {@link #connect(Handler, WifiNanEventCallback)} method instead.
      * <p>
      * Note: a NAN cluster is a shared resource - if the device is already connected to a cluster
      * than this function will simply indicate success immediately.
      *
-     * @param looper The Looper on which to execute all callbacks related to the
+     * @param handler The Handler on whose thread to execute all callbacks related to the
      *            connection - including all sessions opened as part of this
-     *            connection.
+     *            connection. If a null is provided then the application's main thread will be used.
      * @param configRequest The requested NAN configuration.
      * @param callback A callback extended from {@link WifiNanEventCallback}.
      */
-    public void connect(@NonNull Looper looper, @Nullable ConfigRequest configRequest,
+    public void connect(@Nullable Handler handler, @Nullable ConfigRequest configRequest,
             @NonNull WifiNanEventCallback callback) {
         if (VDBG) {
-            Log.v(TAG, "connect(): looper=" + looper + ", callback=" + callback + ", configRequest="
-                    + configRequest);
+            Log.v(TAG,
+                    "connect(): handler=" + handler + ", callback=" + callback + ", configRequest="
+                            + configRequest);
         }
 
         synchronized (mLock) {
-            mLooper = looper;
+            mLooper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
 
             try {
                 mClientId = mService.connect(mBinder, mContext.getOpPackageName(),
-                        new WifiNanEventCallbackProxy(this, looper, callback), configRequest);
+                        new WifiNanEventCallbackProxy(this, mLooper, callback), configRequest);
             } catch (RemoteException e) {
                 mClientId = INVALID_CLIENT_ID;
                 mLooper = null;
@@ -402,7 +403,7 @@
      * connections explicitly before a disconnect.
      * <p>
      * An application may re-connect after a disconnect using
-     * {@link WifiNanManager#connect(Looper, WifiNanEventCallback)} .
+     * {@link WifiNanManager#connect(Handler, WifiNanEventCallback)} .
      */
     public void disconnect() {
         if (VDBG) Log.v(TAG, "disconnect()");