Merge "Move the check for launcher managed profile support."
diff --git a/Android.mk b/Android.mk
index 2d0d1f7..a20798d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -202,6 +202,7 @@
 	core/java/android/os/INetworkActivityListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/IPermissionController.aidl \
+	core/java/android/os/IProcessInfoService.aidl \
 	core/java/android/os/IPowerManager.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/ISchedulingPolicyService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 7ea866a..1abcdb1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -482,7 +482,7 @@
     field public static final int dialogTitle = 16843250; // 0x10101f2
     field public static final int digits = 16843110; // 0x1010166
     field public static final int direction = 16843217; // 0x10101d1
-    field public static final int directionDescriptions = 16843681; // 0x10103a1
+    field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
     field public static final int directionPriority = 16843218; // 0x10101d2
     field public static final int disableDependentsState = 16843249; // 0x10101f1
     field public static final int disabledAlpha = 16842803; // 0x1010033
@@ -529,6 +529,7 @@
     field public static final int ellipsize = 16842923; // 0x10100ab
     field public static final int ems = 16843096; // 0x1010158
     field public static final int enabled = 16842766; // 0x101000e
+    field public static final int end = 16843997; // 0x10104dd
     field public static final int endColor = 16843166; // 0x101019e
     field public static final deprecated int endYear = 16843133; // 0x101017d
     field public static final int enterFadeDuration = 16843532; // 0x101030c
@@ -1014,6 +1015,7 @@
     field public static final int resizeClip = 16843983; // 0x10104cf
     field public static final int resizeMode = 16843619; // 0x1010363
     field public static final int resizeable = 16843405; // 0x101028d
+    field public static final int resizeableActivity = 16843995; // 0x10104db
     field public static final int resource = 16842789; // 0x1010025
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1127,6 +1129,7 @@
     field public static final int stackFromBottom = 16843005; // 0x10100fd
     field public static final int stackViewStyle = 16843838; // 0x101043e
     field public static final int starStyle = 16842882; // 0x1010082
+    field public static final int start = 16843996; // 0x10104dc
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
@@ -1200,7 +1203,7 @@
     field public static final int tag = 16842961; // 0x10100d1
     field public static final int targetActivity = 16843266; // 0x1010202
     field public static final int targetClass = 16842799; // 0x101002f
-    field public static final int targetDescriptions = 16843680; // 0x10103a0
+    field public static final deprecated int targetDescriptions = 16843680; // 0x10103a0
     field public static final int targetId = 16843740; // 0x10103dc
     field public static final int targetName = 16843853; // 0x101044d
     field public static final int targetPackage = 16842785; // 0x1010021
@@ -1316,6 +1319,8 @@
     field public static final int topRightRadius = 16843178; // 0x10101aa
     field public static final int touchscreenBlocksFocus = 16843919; // 0x101048f
     field public static final int track = 16843631; // 0x101036f
+    field public static final int trackTint = 16843993; // 0x10104d9
+    field public static final int trackTintMode = 16843994; // 0x10104da
     field public static final int transcriptMode = 16843008; // 0x1010100
     field public static final int transformPivotX = 16843552; // 0x1010320
     field public static final int transformPivotY = 16843553; // 0x1010321
@@ -1407,6 +1412,7 @@
     field public static final int windowExitTransition = 16843832; // 0x1010438
     field public static final int windowFrame = 16842837; // 0x1010055
     field public static final int windowFullscreen = 16843277; // 0x101020d
+    field public static final int windowHasLightStatusBar = 16843998; // 0x10104de
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
@@ -3914,6 +3920,46 @@
     field public java.lang.String serviceDetails;
   }
 
+  public final class AssistData implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.app.AssistData getAssistData(android.os.Bundle);
+    method public void getWindowAt(int, android.app.AssistData.ViewNode);
+    method public int getWindowCount();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ASSIST_KEY = "android:assist";
+    field public static final android.os.Parcelable.Creator<android.app.AssistData> CREATOR;
+  }
+
+  public static class AssistData.ViewNode {
+    ctor public AssistData.ViewNode();
+    method public void getChildAt(int, android.app.AssistData.ViewNode);
+    method public int getChildCount();
+    method public java.lang.String getClassName();
+    method public java.lang.String getContentDescription();
+    method public android.os.Bundle getExtras();
+    method public int getHeight();
+    method public java.lang.String getHint();
+    method public int getLeft();
+    method public int getScrollX();
+    method public int getScrollY();
+    method public java.lang.String getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public int getTop();
+    method public int getVisibility();
+    method public int getWidth();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActivated();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isLongClickable();
+    method public boolean isSelected();
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -4767,6 +4813,37 @@
     method public android.app.Notification.Builder setWhen(long);
   }
 
+  public static final class Notification.CarExtender implements android.app.Notification.Extender {
+    ctor public Notification.CarExtender();
+    ctor public Notification.CarExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.app.Notification.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.app.Notification.CarExtender setColor(int);
+    method public android.app.Notification.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.app.Notification.CarExtender setUnreadConversation(android.app.Notification.CarExtender.UnreadConversation);
+  }
+
+  public static class Notification.CarExtender.Builder {
+    ctor public Notification.CarExtender.Builder(java.lang.String);
+    method public android.app.Notification.CarExtender.Builder addMessage(java.lang.String);
+    method public android.app.Notification.CarExtender.UnreadConversation build();
+    method public android.app.Notification.CarExtender.Builder setLatestTimestamp(long);
+    method public android.app.Notification.CarExtender.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.app.Notification.CarExtender.Builder setReplyAction(android.app.PendingIntent, android.app.RemoteInput);
+  }
+
+  public static class Notification.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
   public static abstract interface Notification.Extender {
     method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
   }
@@ -5453,6 +5530,7 @@
     field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
     field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
     field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
+    field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
     field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL";
@@ -11911,6 +11989,9 @@
     method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
     method public android.graphics.drawable.Drawable getDrawable(int);
     method public int getId(int);
+    method public int getLayerGravity(int);
+    method public int getLayerHeight(int);
+    method public int getLayerWidth(int);
     method public int getNumberOfLayers();
     method public int getOpacity();
     method public int getPaddingMode();
@@ -11920,7 +12001,10 @@
     method public void setColorFilter(android.graphics.ColorFilter);
     method public boolean setDrawableByLayerId(int, android.graphics.drawable.Drawable);
     method public void setId(int, int);
+    method public void setLayerGravity(int, int);
     method public void setLayerInset(int, int, int, int, int);
+    method public void setLayerInsetRelative(int, int, int, int, int);
+    method public void setLayerSize(int, int, int);
     method public void setOpacity(int);
     method public void setPaddingMode(int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
@@ -12616,6 +12700,8 @@
     field public static final int CAMERA_DISABLED = 1; // 0x1
     field public static final int CAMERA_DISCONNECTED = 2; // 0x2
     field public static final int CAMERA_ERROR = 3; // 0x3
+    field public static final int CAMERA_IN_USE = 4; // 0x4
+    field public static final int MAX_CAMERAS_IN_USE = 5; // 0x5
   }
 
   public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
@@ -12659,11 +12745,14 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
@@ -12682,6 +12771,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -12756,7 +12846,10 @@
     method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
     method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+    method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+    method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
     method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
+    method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
   }
 
   public static abstract class CameraManager.AvailabilityCallback {
@@ -12765,6 +12858,13 @@
     method public void onCameraUnavailable(java.lang.String);
   }
 
+  public static abstract class CameraManager.TorchCallback {
+    ctor public CameraManager.TorchCallback();
+    method public void onTorchModeAvailable(java.lang.String);
+    method public void onTorchModeChanged(java.lang.String, boolean);
+    method public void onTorchModeUnavailable(java.lang.String);
+  }
+
   public abstract class CameraMetadata {
     method public java.util.List<TKey> getKeys();
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
@@ -12889,13 +12989,16 @@
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -13001,6 +13104,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
@@ -13078,6 +13182,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
     field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -14117,6 +14222,7 @@
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
     method public boolean isSpeakerphoneOn();
+    method public boolean isStreamMute(int);
     method public boolean isVolumeFixed();
     method public deprecated boolean isWiredHeadsetOn();
     method public void loadSoundEffects();
@@ -14135,8 +14241,8 @@
     method public void setRingerMode(int);
     method public deprecated void setRouting(int, int, int);
     method public void setSpeakerphoneOn(boolean);
-    method public void setStreamMute(int, boolean);
-    method public void setStreamSolo(int, boolean);
+    method public deprecated void setStreamMute(int, boolean);
+    method public deprecated void setStreamSolo(int, boolean);
     method public void setStreamVolume(int, int, int);
     method public deprecated void setVibrateSetting(int, int);
     method public deprecated void setWiredHeadsetOn(boolean);
@@ -14154,8 +14260,11 @@
     field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
     field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
     field public static final int ADJUST_LOWER = -1; // 0xffffffff
+    field public static final int ADJUST_MUTE = -100; // 0xffffff9c
     field public static final int ADJUST_RAISE = 1; // 0x1
     field public static final int ADJUST_SAME = 0; // 0x0
+    field public static final int ADJUST_TOGGLE_MUTE = 101; // 0x65
+    field public static final int ADJUST_UNMUTE = 100; // 0x64
     field public static final int AUDIOFOCUS_GAIN = 1; // 0x1
     field public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; // 0x2
     field public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4; // 0x4
@@ -16205,6 +16314,7 @@
     method public void connect();
     method public void disconnect();
     method public android.os.Bundle getExtras();
+    method public void getMediaItem(java.lang.String, android.media.browse.MediaBrowser.MediaItemCallback);
     method public java.lang.String getRoot();
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
@@ -16234,6 +16344,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.MediaItemCallback {
+    ctor public MediaBrowser.MediaItemCallback();
+    method public void onError();
+    method public void onMediaItemLoaded(android.media.browse.MediaBrowser.MediaItem);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -17559,7 +17675,6 @@
     method public static javax.net.SocketFactory getDefault(int);
     method public static javax.net.ssl.SSLSocketFactory getDefault(int, android.net.SSLSessionCache);
     method public java.lang.String[] getDefaultCipherSuites();
-    method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
     method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
     method public byte[] getNpnSelectedProtocol(java.net.Socket);
     method public java.lang.String[] getSupportedCipherSuites();
@@ -21770,6 +21885,7 @@
     field public static final int KITKAT_WATCH = 20; // 0x14
     field public static final int LOLLIPOP = 21; // 0x15
     field public static final int LOLLIPOP_MR1 = 22; // 0x16
+    field public static final int MNC = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -23158,6 +23274,7 @@
   public final class PrintAttributes implements android.os.Parcelable {
     method public int describeContents();
     method public int getColorMode();
+    method public int getDuplexMode();
     method public android.print.PrintAttributes.MediaSize getMediaSize();
     method public android.print.PrintAttributes.Margins getMinMargins();
     method public android.print.PrintAttributes.Resolution getResolution();
@@ -23165,12 +23282,16 @@
     field public static final int COLOR_MODE_COLOR = 2; // 0x2
     field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.print.PrintAttributes> CREATOR;
+    field public static final int DUPLEX_MODE_LONG_EDGE = 2; // 0x2
+    field public static final int DUPLEX_MODE_NONE = 1; // 0x1
+    field public static final int DUPLEX_MODE_SHORT_EDGE = 4; // 0x4
   }
 
   public static final class PrintAttributes.Builder {
     ctor public PrintAttributes.Builder();
     method public android.print.PrintAttributes build();
     method public android.print.PrintAttributes.Builder setColorMode(int);
+    method public android.print.PrintAttributes.Builder setDuplexMode(int);
     method public android.print.PrintAttributes.Builder setMediaSize(android.print.PrintAttributes.MediaSize);
     method public android.print.PrintAttributes.Builder setMinMargins(android.print.PrintAttributes.Margins);
     method public android.print.PrintAttributes.Builder setResolution(android.print.PrintAttributes.Resolution);
@@ -23388,6 +23509,7 @@
     method public int describeContents();
     method public int getColorModes();
     method public android.print.PrintAttributes getDefaults();
+    method public int getDuplexModes();
     method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes();
     method public android.print.PrintAttributes.Margins getMinMargins();
     method public java.util.List<android.print.PrintAttributes.Resolution> getResolutions();
@@ -23401,6 +23523,7 @@
     method public android.print.PrinterCapabilitiesInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
     method public android.print.PrinterCapabilitiesInfo build();
     method public android.print.PrinterCapabilitiesInfo.Builder setColorModes(int, int);
+    method public android.print.PrinterCapabilitiesInfo.Builder setDuplexModes(int, int);
     method public android.print.PrinterCapabilitiesInfo.Builder setMinMargins(android.print.PrintAttributes.Margins);
   }
 
@@ -24597,6 +24720,7 @@
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
+    field public static final java.lang.String QUERY_PARAMETER_VCARD_NO_PHOTO = "no_photo";
   }
 
   public static final class ContactsContract.Contacts.AggregationSuggestions implements android.provider.BaseColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns {
@@ -24911,6 +25035,7 @@
     method public static void showQuickContact(android.content.Context, android.graphics.Rect, android.net.Uri, int, java.lang.String[]);
     field public static final java.lang.String ACTION_QUICK_CONTACT = "android.provider.action.QUICK_CONTACT";
     field public static final java.lang.String EXTRA_EXCLUDE_MIMES = "android.provider.extra.EXCLUDE_MIMES";
+    field public static final java.lang.String EXTRA_MODE = "android.provider.extra.MODE";
     field public static final int MODE_LARGE = 3; // 0x3
     field public static final int MODE_MEDIUM = 2; // 0x2
     field public static final int MODE_SMALL = 1; // 0x1
@@ -27348,6 +27473,7 @@
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void getMediaItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>) throws java.lang.UnsupportedOperationException;
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public android.os.IBinder onBind(android.content.Intent);
@@ -28539,8 +28665,65 @@
 
 package android.telecom {
 
+  public class PhoneAccount implements android.os.Parcelable {
+    method public static android.telecom.PhoneAccount.Builder builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
+    method public android.graphics.drawable.Drawable createIconDrawable(android.content.Context);
+    method public int describeContents();
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public android.net.Uri getAddress();
+    method public int getCapabilities();
+    method public int getHighlightColor();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public java.lang.String getIconPackageName();
+    method public int getIconResId();
+    method public int getIconTint();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.CharSequence getShortDescription();
+    method public android.net.Uri getSubscriptionAddress();
+    method public java.util.List<java.lang.String> getSupportedUriSchemes();
+    method public boolean hasCapabilities(int);
+    method public boolean supportsUriScheme(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+    field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+    field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
+    field public static final int NO_ICON_TINT = 0; // 0x0
+    field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
+    field public static final java.lang.String SCHEME_SIP = "sip";
+    field public static final java.lang.String SCHEME_TEL = "tel";
+    field public static final java.lang.String SCHEME_VOICEMAIL = "voicemail";
+  }
+
+  public static class PhoneAccount.Builder {
+    ctor public PhoneAccount.Builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
+    ctor public PhoneAccount.Builder(android.telecom.PhoneAccount);
+    method public android.telecom.PhoneAccount build();
+    method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
+    method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+    method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
+    method public android.telecom.PhoneAccount.Builder setIcon(android.content.Context, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(java.lang.String, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(android.content.Context, int, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(java.lang.String, int, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.Bitmap);
+    method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
+    method public android.telecom.PhoneAccount.Builder setSubscriptionAddress(android.net.Uri);
+    method public android.telecom.PhoneAccount.Builder setSupportedUriSchemes(java.util.List<java.lang.String>);
+  }
+
+  public class PhoneAccountHandle implements android.os.Parcelable {
+    ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String);
+    method public int describeContents();
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
+  }
+
   public class TelecomManager {
     method public void cancelMissedCallsNotification();
+    method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
     method public boolean isInCall();
     method public void showInCallScreen(boolean);
@@ -28549,7 +28732,10 @@
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+    field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
+    field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
+    field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -28558,6 +28744,34 @@
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
   }
 
+  public class VideoProfile implements android.os.Parcelable {
+    ctor public VideoProfile(int);
+    ctor public VideoProfile(int, int);
+    method public int describeContents();
+    method public int getQuality();
+    method public int getVideoState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.VideoProfile> CREATOR;
+    field public static final int QUALITY_DEFAULT = 4; // 0x4
+    field public static final int QUALITY_HIGH = 1; // 0x1
+    field public static final int QUALITY_LOW = 3; // 0x3
+    field public static final int QUALITY_MEDIUM = 2; // 0x2
+  }
+
+  public static class VideoProfile.VideoState {
+    ctor public VideoProfile.VideoState();
+    method public static boolean isAudioOnly(int);
+    method public static boolean isBidirectional(int);
+    method public static boolean isPaused(int);
+    method public static boolean isReceptionEnabled(int);
+    method public static boolean isTransmissionEnabled(int);
+    field public static final int AUDIO_ONLY = 0; // 0x0
+    field public static final int BIDIRECTIONAL = 3; // 0x3
+    field public static final int PAUSED = 4; // 0x4
+    field public static final int RX_ENABLED = 2; // 0x2
+    field public static final int TX_ENABLED = 1; // 0x1
+  }
+
 }
 
 package android.telephony {
@@ -28751,6 +28965,7 @@
 
   public class PhoneNumberUtils {
     ctor public PhoneNumberUtils();
+    method public static void addPhoneTtsSpan(android.text.Spannable, int, int);
     method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
     method public static java.lang.String calledPartyBCDToString(byte[], int, int);
     method public static boolean compare(java.lang.String, java.lang.String);
@@ -28768,6 +28983,8 @@
     method public static java.lang.String formatNumberToRFC3966(java.lang.String, java.lang.String);
     method public static deprecated int getFormatTypeForLocale(java.util.Locale);
     method public static java.lang.String getNumberFromIntent(android.content.Intent, android.content.Context);
+    method public static android.text.style.TtsSpan getPhoneTtsSpan(java.lang.String);
+    method public static java.lang.CharSequence getPhoneTtsSpannable(java.lang.CharSequence);
     method public static java.lang.String getStrippedReversed(java.lang.String);
     method public static final boolean is12Key(char);
     method public static final boolean isDialable(char);
@@ -32383,6 +32600,7 @@
     method public java.lang.Object getTag();
     method public abstract java.lang.CharSequence getTitle();
     method public boolean getTitleOptionalHint();
+    method public int getType();
     method public abstract void invalidate();
     method public boolean isTitleOptional();
     method public abstract void setCustomView(android.view.View);
@@ -32392,6 +32610,9 @@
     method public abstract void setTitle(java.lang.CharSequence);
     method public abstract void setTitle(int);
     method public void setTitleOptionalHint(boolean);
+    method public void setType(int);
+    field public static final int TYPE_FLOATING = 1; // 0x1
+    field public static final int TYPE_PRIMARY = 0; // 0x0
   }
 
   public static abstract interface ActionMode.Callback {
@@ -33742,6 +33963,7 @@
     method public android.view.View focusSearch(int);
     method public void forceLayout();
     method public static int generateViewId();
+    method public java.lang.CharSequence getAccessibilityClassName();
     method public int getAccessibilityLiveRegion();
     method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
     method public int getAccessibilityTraversalAfter();
@@ -33967,6 +34189,7 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onProvideAssistData(android.view.ViewAssistData, android.os.Bundle);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
     method protected android.os.Parcelable onSaveInstanceState();
@@ -34084,6 +34307,7 @@
     method public void setOnHoverListener(android.view.View.OnHoverListener);
     method public void setOnKeyListener(android.view.View.OnKeyListener);
     method public void setOnLongClickListener(android.view.View.OnLongClickListener);
+    method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
     method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
     method public void setOnTouchListener(android.view.View.OnTouchListener);
     method public void setOutlineProvider(android.view.ViewOutlineProvider);
@@ -34241,6 +34465,7 @@
     field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
     field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
     field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
+    field public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
@@ -34351,6 +34576,10 @@
     method public abstract boolean onLongClick(android.view.View);
   }
 
+  public static abstract interface View.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.view.View, int, int, int, int);
+  }
+
   public static abstract interface View.OnSystemUiVisibilityChangeListener {
     method public abstract void onSystemUiVisibilityChange(int);
   }
@@ -34363,6 +34592,17 @@
     method public static android.animation.Animator createCircularReveal(android.view.View, int, int, float, float);
   }
 
+  public abstract class ViewAssistData {
+    ctor public ViewAssistData();
+    method public abstract java.lang.CharSequence getHint();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract int getTextSelectionEnd();
+    method public abstract int getTextSelectionStart();
+    method public abstract void setHint(java.lang.CharSequence);
+    method public abstract void setText(java.lang.CharSequence);
+    method public abstract void setText(java.lang.CharSequence, int, int);
+  }
+
   public class ViewConfiguration {
     ctor public deprecated ViewConfiguration();
     method public static android.view.ViewConfiguration get(android.content.Context);
@@ -38390,6 +38630,8 @@
     method public void setClippingEnabled(boolean);
     method public void setContentView(android.view.View);
     method public void setElevation(float);
+    method public void setEnterTransition(android.transition.Transition);
+    method public void setExitTransition(android.transition.Transition);
     method public void setFocusable(boolean);
     method public void setHeight(int);
     method public void setIgnoreCheekPress();
@@ -38991,7 +39233,11 @@
     method public java.lang.CharSequence getTextOn();
     method public android.graphics.drawable.Drawable getThumbDrawable();
     method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
     method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
     method public void onMeasure(int, int);
     method public void setShowText(boolean);
     method public void setSplitTrack(boolean);
@@ -39005,8 +39251,12 @@
     method public void setThumbDrawable(android.graphics.drawable.Drawable);
     method public void setThumbResource(int);
     method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
     method public void setTrackDrawable(android.graphics.drawable.Drawable);
     method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/api/removed.txt b/api/removed.txt
index 1b69ee8..9322973 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -6,6 +6,14 @@
 
 }
 
+package android.net {
+
+  public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
+    method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
+  }
+
+}
+
 package android.os {
 
   public final class PowerManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 05100b0..d971ce7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -554,7 +554,7 @@
     field public static final int dialogTitle = 16843250; // 0x10101f2
     field public static final int digits = 16843110; // 0x1010166
     field public static final int direction = 16843217; // 0x10101d1
-    field public static final int directionDescriptions = 16843681; // 0x10103a1
+    field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
     field public static final int directionPriority = 16843218; // 0x10101d2
     field public static final int disableDependentsState = 16843249; // 0x10101f1
     field public static final int disabledAlpha = 16842803; // 0x1010033
@@ -601,6 +601,7 @@
     field public static final int ellipsize = 16842923; // 0x10100ab
     field public static final int ems = 16843096; // 0x1010158
     field public static final int enabled = 16842766; // 0x101000e
+    field public static final int end = 16843997; // 0x10104dd
     field public static final int endColor = 16843166; // 0x101019e
     field public static final deprecated int endYear = 16843133; // 0x101017d
     field public static final int enterFadeDuration = 16843532; // 0x101030c
@@ -1086,6 +1087,7 @@
     field public static final int resizeClip = 16843983; // 0x10104cf
     field public static final int resizeMode = 16843619; // 0x1010363
     field public static final int resizeable = 16843405; // 0x101028d
+    field public static final int resizeableActivity = 16843995; // 0x10104db
     field public static final int resource = 16842789; // 0x1010025
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1203,6 +1205,7 @@
     field public static final int stackFromBottom = 16843005; // 0x10100fd
     field public static final int stackViewStyle = 16843838; // 0x101043e
     field public static final int starStyle = 16842882; // 0x1010082
+    field public static final int start = 16843996; // 0x10104dc
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
@@ -1276,7 +1279,7 @@
     field public static final int tag = 16842961; // 0x10100d1
     field public static final int targetActivity = 16843266; // 0x1010202
     field public static final int targetClass = 16842799; // 0x101002f
-    field public static final int targetDescriptions = 16843680; // 0x10103a0
+    field public static final deprecated int targetDescriptions = 16843680; // 0x10103a0
     field public static final int targetId = 16843740; // 0x10103dc
     field public static final int targetName = 16843853; // 0x101044d
     field public static final int targetPackage = 16842785; // 0x1010021
@@ -1392,6 +1395,8 @@
     field public static final int topRightRadius = 16843178; // 0x10101aa
     field public static final int touchscreenBlocksFocus = 16843919; // 0x101048f
     field public static final int track = 16843631; // 0x101036f
+    field public static final int trackTint = 16843993; // 0x10104d9
+    field public static final int trackTintMode = 16843994; // 0x10104da
     field public static final int transcriptMode = 16843008; // 0x1010100
     field public static final int transformPivotX = 16843552; // 0x1010320
     field public static final int transformPivotY = 16843553; // 0x1010321
@@ -1483,6 +1488,7 @@
     field public static final int windowExitTransition = 16843832; // 0x1010438
     field public static final int windowFrame = 16842837; // 0x1010055
     field public static final int windowFullscreen = 16843277; // 0x101020d
+    field public static final int windowHasLightStatusBar = 16843998; // 0x10104de
     field public static final int windowHideAnimation = 16842935; // 0x10100b7
     field public static final int windowIsFloating = 16842839; // 0x1010057
     field public static final int windowIsTranslucent = 16842840; // 0x1010058
@@ -4004,6 +4010,46 @@
     field public java.lang.String serviceDetails;
   }
 
+  public final class AssistData implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.app.AssistData getAssistData(android.os.Bundle);
+    method public void getWindowAt(int, android.app.AssistData.ViewNode);
+    method public int getWindowCount();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ASSIST_KEY = "android:assist";
+    field public static final android.os.Parcelable.Creator<android.app.AssistData> CREATOR;
+  }
+
+  public static class AssistData.ViewNode {
+    ctor public AssistData.ViewNode();
+    method public void getChildAt(int, android.app.AssistData.ViewNode);
+    method public int getChildCount();
+    method public java.lang.String getClassName();
+    method public java.lang.String getContentDescription();
+    method public android.os.Bundle getExtras();
+    method public int getHeight();
+    method public java.lang.String getHint();
+    method public int getLeft();
+    method public int getScrollX();
+    method public int getScrollY();
+    method public java.lang.String getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public int getTop();
+    method public int getVisibility();
+    method public int getWidth();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActivated();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isLongClickable();
+    method public boolean isSelected();
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -4857,6 +4903,37 @@
     method public android.app.Notification.Builder setWhen(long);
   }
 
+  public static final class Notification.CarExtender implements android.app.Notification.Extender {
+    ctor public Notification.CarExtender();
+    ctor public Notification.CarExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public int getColor();
+    method public android.graphics.Bitmap getLargeIcon();
+    method public android.app.Notification.CarExtender.UnreadConversation getUnreadConversation();
+    method public android.app.Notification.CarExtender setColor(int);
+    method public android.app.Notification.CarExtender setLargeIcon(android.graphics.Bitmap);
+    method public android.app.Notification.CarExtender setUnreadConversation(android.app.Notification.CarExtender.UnreadConversation);
+  }
+
+  public static class Notification.CarExtender.Builder {
+    ctor public Notification.CarExtender.Builder(java.lang.String);
+    method public android.app.Notification.CarExtender.Builder addMessage(java.lang.String);
+    method public android.app.Notification.CarExtender.UnreadConversation build();
+    method public android.app.Notification.CarExtender.Builder setLatestTimestamp(long);
+    method public android.app.Notification.CarExtender.Builder setReadPendingIntent(android.app.PendingIntent);
+    method public android.app.Notification.CarExtender.Builder setReplyAction(android.app.PendingIntent, android.app.RemoteInput);
+  }
+
+  public static class Notification.CarExtender.UnreadConversation {
+    method public long getLatestTimestamp();
+    method public java.lang.String[] getMessages();
+    method public java.lang.String getParticipant();
+    method public java.lang.String[] getParticipants();
+    method public android.app.PendingIntent getReadPendingIntent();
+    method public android.app.RemoteInput getRemoteInput();
+    method public android.app.PendingIntent getReplyPendingIntent();
+  }
+
   public static abstract interface Notification.Extender {
     method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
   }
@@ -5334,6 +5411,7 @@
 
   public class WallpaperManager {
     method public void clear() throws java.io.IOException;
+    method public void clearWallpaper();
     method public void clearWallpaperOffsets(android.os.IBinder);
     method public void forgetLoadedWallpaper();
     method public android.graphics.drawable.Drawable getBuiltInDrawable();
@@ -5354,6 +5432,7 @@
     method public void setDisplayPadding(android.graphics.Rect);
     method public void setResource(int) throws java.io.IOException;
     method public void setStream(java.io.InputStream) throws java.io.IOException;
+    method public boolean setWallpaperComponent(android.content.ComponentName);
     method public void setWallpaperOffsetSteps(float, float);
     method public void setWallpaperOffsets(android.os.IBinder, float, float);
     method public void suggestDesiredDimensions(int, int);
@@ -5554,6 +5633,7 @@
     field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
     field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
     field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
+    field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
     field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL";
@@ -12179,6 +12259,9 @@
     method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
     method public android.graphics.drawable.Drawable getDrawable(int);
     method public int getId(int);
+    method public int getLayerGravity(int);
+    method public int getLayerHeight(int);
+    method public int getLayerWidth(int);
     method public int getNumberOfLayers();
     method public int getOpacity();
     method public int getPaddingMode();
@@ -12188,7 +12271,10 @@
     method public void setColorFilter(android.graphics.ColorFilter);
     method public boolean setDrawableByLayerId(int, android.graphics.drawable.Drawable);
     method public void setId(int, int);
+    method public void setLayerGravity(int, int);
     method public void setLayerInset(int, int, int, int, int);
+    method public void setLayerInsetRelative(int, int, int, int, int);
+    method public void setLayerSize(int, int, int);
     method public void setOpacity(int);
     method public void setPaddingMode(int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
@@ -12884,6 +12970,8 @@
     field public static final int CAMERA_DISABLED = 1; // 0x1
     field public static final int CAMERA_DISCONNECTED = 2; // 0x2
     field public static final int CAMERA_ERROR = 3; // 0x3
+    field public static final int CAMERA_IN_USE = 4; // 0x4
+    field public static final int MAX_CAMERAS_IN_USE = 5; // 0x5
   }
 
   public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
@@ -12927,11 +13015,14 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
@@ -12950,6 +13041,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -13024,7 +13116,10 @@
     method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
     method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+    method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+    method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
     method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
+    method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
   }
 
   public static abstract class CameraManager.AvailabilityCallback {
@@ -13033,6 +13128,13 @@
     method public void onCameraUnavailable(java.lang.String);
   }
 
+  public static abstract class CameraManager.TorchCallback {
+    ctor public CameraManager.TorchCallback();
+    method public void onTorchModeAvailable(java.lang.String);
+    method public void onTorchModeChanged(java.lang.String, boolean);
+    method public void onTorchModeUnavailable(java.lang.String);
+  }
+
   public abstract class CameraMetadata {
     method public java.util.List<TKey> getKeys();
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
@@ -13157,13 +13259,16 @@
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -13269,6 +13374,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
@@ -13346,6 +13452,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
     field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -15095,6 +15202,7 @@
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
     method public boolean isSpeakerphoneOn();
+    method public boolean isStreamMute(int);
     method public boolean isVolumeFixed();
     method public deprecated boolean isWiredHeadsetOn();
     method public void loadSoundEffects();
@@ -15116,8 +15224,8 @@
     method public void setRingerMode(int);
     method public deprecated void setRouting(int, int, int);
     method public void setSpeakerphoneOn(boolean);
-    method public void setStreamMute(int, boolean);
-    method public void setStreamSolo(int, boolean);
+    method public deprecated void setStreamMute(int, boolean);
+    method public deprecated void setStreamSolo(int, boolean);
     method public void setStreamVolume(int, int, int);
     method public deprecated void setVibrateSetting(int, int);
     method public deprecated void setWiredHeadsetOn(boolean);
@@ -15136,8 +15244,11 @@
     field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
     field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
     field public static final int ADJUST_LOWER = -1; // 0xffffffff
+    field public static final int ADJUST_MUTE = -100; // 0xffffff9c
     field public static final int ADJUST_RAISE = 1; // 0x1
     field public static final int ADJUST_SAME = 0; // 0x0
+    field public static final int ADJUST_TOGGLE_MUTE = 101; // 0x65
+    field public static final int ADJUST_UNMUTE = 100; // 0x64
     field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
     field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
@@ -17255,6 +17366,7 @@
     method public void connect();
     method public void disconnect();
     method public android.os.Bundle getExtras();
+    method public void getMediaItem(java.lang.String, android.media.browse.MediaBrowser.MediaItemCallback);
     method public java.lang.String getRoot();
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
@@ -17284,6 +17396,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.MediaItemCallback {
+    ctor public MediaBrowser.MediaItemCallback();
+    method public void onError();
+    method public void onMediaItemLoaded(android.media.browse.MediaBrowser.MediaItem);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -18819,7 +18937,6 @@
     method public static javax.net.SocketFactory getDefault(int);
     method public static javax.net.ssl.SSLSocketFactory getDefault(int, android.net.SSLSessionCache);
     method public java.lang.String[] getDefaultCipherSuites();
-    method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
     method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
     method public byte[] getNpnSelectedProtocol(java.net.Socket);
     method public java.lang.String[] getSupportedCipherSuites();
@@ -23349,6 +23466,7 @@
     field public static final int KITKAT_WATCH = 20; // 0x14
     field public static final int LOLLIPOP = 21; // 0x15
     field public static final int LOLLIPOP_MR1 = 22; // 0x16
+    field public static final int MNC = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -24747,6 +24865,7 @@
   public final class PrintAttributes implements android.os.Parcelable {
     method public int describeContents();
     method public int getColorMode();
+    method public int getDuplexMode();
     method public android.print.PrintAttributes.MediaSize getMediaSize();
     method public android.print.PrintAttributes.Margins getMinMargins();
     method public android.print.PrintAttributes.Resolution getResolution();
@@ -24754,12 +24873,16 @@
     field public static final int COLOR_MODE_COLOR = 2; // 0x2
     field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.print.PrintAttributes> CREATOR;
+    field public static final int DUPLEX_MODE_LONG_EDGE = 2; // 0x2
+    field public static final int DUPLEX_MODE_NONE = 1; // 0x1
+    field public static final int DUPLEX_MODE_SHORT_EDGE = 4; // 0x4
   }
 
   public static final class PrintAttributes.Builder {
     ctor public PrintAttributes.Builder();
     method public android.print.PrintAttributes build();
     method public android.print.PrintAttributes.Builder setColorMode(int);
+    method public android.print.PrintAttributes.Builder setDuplexMode(int);
     method public android.print.PrintAttributes.Builder setMediaSize(android.print.PrintAttributes.MediaSize);
     method public android.print.PrintAttributes.Builder setMinMargins(android.print.PrintAttributes.Margins);
     method public android.print.PrintAttributes.Builder setResolution(android.print.PrintAttributes.Resolution);
@@ -24977,6 +25100,7 @@
     method public int describeContents();
     method public int getColorModes();
     method public android.print.PrintAttributes getDefaults();
+    method public int getDuplexModes();
     method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes();
     method public android.print.PrintAttributes.Margins getMinMargins();
     method public java.util.List<android.print.PrintAttributes.Resolution> getResolutions();
@@ -24990,6 +25114,7 @@
     method public android.print.PrinterCapabilitiesInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
     method public android.print.PrinterCapabilitiesInfo build();
     method public android.print.PrinterCapabilitiesInfo.Builder setColorModes(int, int);
+    method public android.print.PrinterCapabilitiesInfo.Builder setDuplexModes(int, int);
     method public android.print.PrinterCapabilitiesInfo.Builder setMinMargins(android.print.PrintAttributes.Margins);
   }
 
@@ -26186,6 +26311,7 @@
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
+    field public static final java.lang.String QUERY_PARAMETER_VCARD_NO_PHOTO = "no_photo";
   }
 
   public static final class ContactsContract.Contacts.AggregationSuggestions implements android.provider.BaseColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns {
@@ -26500,6 +26626,7 @@
     method public static void showQuickContact(android.content.Context, android.graphics.Rect, android.net.Uri, int, java.lang.String[]);
     field public static final java.lang.String ACTION_QUICK_CONTACT = "android.provider.action.QUICK_CONTACT";
     field public static final java.lang.String EXTRA_EXCLUDE_MIMES = "android.provider.extra.EXCLUDE_MIMES";
+    field public static final java.lang.String EXTRA_MODE = "android.provider.extra.MODE";
     field public static final int MODE_LARGE = 3; // 0x3
     field public static final int MODE_MEDIUM = 2; // 0x2
     field public static final int MODE_SMALL = 1; // 0x1
@@ -27201,6 +27328,7 @@
     field public static final java.lang.String SHOW_PROCESSES = "show_processes";
     field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
     field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
+    field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
     field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
     field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
     field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
@@ -28937,6 +29065,7 @@
   public abstract class MediaBrowserService extends android.app.Service {
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public void getMediaItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>) throws java.lang.UnsupportedOperationException;
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public android.os.IBinder onBind(android.content.Intent);
@@ -30377,6 +30506,7 @@
     method public final int getState();
     method public final android.telecom.StatusHints getStatusHints();
     method public final boolean isRingbackRequested();
+    method protected void notifyConferenceStarted();
     method public void onAbort();
     method public void onAnswer();
     method public void onAudioStateChanged(android.telecom.AudioState);
@@ -30402,6 +30532,7 @@
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setInitialized();
     method public final void setInitializing();
+    method public final void setNextPostDialChar(char);
     method public final void setOnHold();
     method public final void setPostDialWait(java.lang.String);
     method public final void setRingbackRequested(boolean);
@@ -30541,10 +30672,14 @@
     method public java.util.List<java.lang.String> getSupportedUriSchemes();
     method public boolean hasCapabilities(int);
     method public boolean supportsUriScheme(java.lang.String);
+    method public android.telecom.PhoneAccount.Builder toBuilder();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
+    field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
     field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
     field public static final int NO_ICON_TINT = 0; // 0x0
@@ -30557,7 +30692,9 @@
   public static class PhoneAccount.Builder {
     ctor public PhoneAccount.Builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
     ctor public PhoneAccount.Builder(android.telecom.PhoneAccount);
+    method public android.telecom.PhoneAccount.Builder addSupportedUriScheme(java.lang.String);
     method public android.telecom.PhoneAccount build();
+    method public android.telecom.PhoneAccount.Builder setAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
     method public android.telecom.PhoneAccount.Builder setCapabilities(int);
     method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
@@ -30573,9 +30710,11 @@
 
   public class PhoneAccountHandle implements android.os.Parcelable {
     ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String);
+    ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String, android.os.UserHandle);
     method public int describeContents();
     method public android.content.ComponentName getComponentName();
     method public java.lang.String getId();
+    method public android.os.UserHandle getUserHandle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
   }
@@ -30711,6 +30850,7 @@
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
+    field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -30719,6 +30859,34 @@
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
   }
 
+  public class VideoProfile implements android.os.Parcelable {
+    ctor public VideoProfile(int);
+    ctor public VideoProfile(int, int);
+    method public int describeContents();
+    method public int getQuality();
+    method public int getVideoState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.VideoProfile> CREATOR;
+    field public static final int QUALITY_DEFAULT = 4; // 0x4
+    field public static final int QUALITY_HIGH = 1; // 0x1
+    field public static final int QUALITY_LOW = 3; // 0x3
+    field public static final int QUALITY_MEDIUM = 2; // 0x2
+  }
+
+  public static class VideoProfile.VideoState {
+    ctor public VideoProfile.VideoState();
+    method public static boolean isAudioOnly(int);
+    method public static boolean isBidirectional(int);
+    method public static boolean isPaused(int);
+    method public static boolean isReceptionEnabled(int);
+    method public static boolean isTransmissionEnabled(int);
+    field public static final int AUDIO_ONLY = 0; // 0x0
+    field public static final int BIDIRECTIONAL = 3; // 0x3
+    field public static final int PAUSED = 4; // 0x4
+    field public static final int RX_ENABLED = 2; // 0x2
+    field public static final int TX_ENABLED = 1; // 0x1
+  }
+
 }
 
 package android.telephony {
@@ -30912,6 +31080,7 @@
 
   public class PhoneNumberUtils {
     ctor public PhoneNumberUtils();
+    method public static void addPhoneTtsSpan(android.text.Spannable, int, int);
     method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
     method public static java.lang.String calledPartyBCDToString(byte[], int, int);
     method public static boolean compare(java.lang.String, java.lang.String);
@@ -30929,6 +31098,8 @@
     method public static java.lang.String formatNumberToRFC3966(java.lang.String, java.lang.String);
     method public static deprecated int getFormatTypeForLocale(java.util.Locale);
     method public static java.lang.String getNumberFromIntent(android.content.Intent, android.content.Context);
+    method public static android.text.style.TtsSpan getPhoneTtsSpan(java.lang.String);
+    method public static java.lang.CharSequence getPhoneTtsSpannable(java.lang.CharSequence);
     method public static java.lang.String getStrippedReversed(java.lang.String);
     method public static final boolean is12Key(char);
     method public static final boolean isDialable(char);
@@ -34582,6 +34753,7 @@
     method public java.lang.Object getTag();
     method public abstract java.lang.CharSequence getTitle();
     method public boolean getTitleOptionalHint();
+    method public int getType();
     method public abstract void invalidate();
     method public boolean isTitleOptional();
     method public abstract void setCustomView(android.view.View);
@@ -34591,6 +34763,9 @@
     method public abstract void setTitle(java.lang.CharSequence);
     method public abstract void setTitle(int);
     method public void setTitleOptionalHint(boolean);
+    method public void setType(int);
+    field public static final int TYPE_FLOATING = 1; // 0x1
+    field public static final int TYPE_PRIMARY = 0; // 0x0
   }
 
   public static abstract interface ActionMode.Callback {
@@ -35941,6 +36116,7 @@
     method public android.view.View focusSearch(int);
     method public void forceLayout();
     method public static int generateViewId();
+    method public java.lang.CharSequence getAccessibilityClassName();
     method public int getAccessibilityLiveRegion();
     method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
     method public int getAccessibilityTraversalAfter();
@@ -36166,6 +36342,7 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onProvideAssistData(android.view.ViewAssistData, android.os.Bundle);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
     method protected android.os.Parcelable onSaveInstanceState();
@@ -36283,6 +36460,7 @@
     method public void setOnHoverListener(android.view.View.OnHoverListener);
     method public void setOnKeyListener(android.view.View.OnKeyListener);
     method public void setOnLongClickListener(android.view.View.OnLongClickListener);
+    method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
     method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
     method public void setOnTouchListener(android.view.View.OnTouchListener);
     method public void setOutlineProvider(android.view.ViewOutlineProvider);
@@ -36440,6 +36618,7 @@
     field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
     field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
     field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
+    field public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
@@ -36550,6 +36729,10 @@
     method public abstract boolean onLongClick(android.view.View);
   }
 
+  public static abstract interface View.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.view.View, int, int, int, int);
+  }
+
   public static abstract interface View.OnSystemUiVisibilityChangeListener {
     method public abstract void onSystemUiVisibilityChange(int);
   }
@@ -36562,6 +36745,17 @@
     method public static android.animation.Animator createCircularReveal(android.view.View, int, int, float, float);
   }
 
+  public abstract class ViewAssistData {
+    ctor public ViewAssistData();
+    method public abstract java.lang.CharSequence getHint();
+    method public abstract java.lang.CharSequence getText();
+    method public abstract int getTextSelectionEnd();
+    method public abstract int getTextSelectionStart();
+    method public abstract void setHint(java.lang.CharSequence);
+    method public abstract void setText(java.lang.CharSequence);
+    method public abstract void setText(java.lang.CharSequence, int, int);
+  }
+
   public class ViewConfiguration {
     ctor public deprecated ViewConfiguration();
     method public static android.view.ViewConfiguration get(android.content.Context);
@@ -37260,8 +37454,10 @@
     method public java.lang.String debug(java.lang.String);
     method public int describeContents();
     method public final java.lang.CharSequence getTitle();
+    method public final long getUserActivityTimeout();
     method public static boolean mayUseInputMethod(int);
     method public final void setTitle(java.lang.CharSequence);
+    method public final void setUserActivityTimeout(long);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ALPHA_CHANGED = 128; // 0x80
     field public static final int ANIMATION_CHANGED = 16; // 0x10
@@ -40885,6 +41081,8 @@
     method public void setClippingEnabled(boolean);
     method public void setContentView(android.view.View);
     method public void setElevation(float);
+    method public void setEnterTransition(android.transition.Transition);
+    method public void setExitTransition(android.transition.Transition);
     method public void setFocusable(boolean);
     method public void setHeight(int);
     method public void setIgnoreCheekPress();
@@ -41486,7 +41684,11 @@
     method public java.lang.CharSequence getTextOn();
     method public android.graphics.drawable.Drawable getThumbDrawable();
     method public int getThumbTextPadding();
+    method public android.content.res.ColorStateList getThumbTintList();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
     method public android.graphics.drawable.Drawable getTrackDrawable();
+    method public android.content.res.ColorStateList getTrackTintList();
+    method public android.graphics.PorterDuff.Mode getTrackTintMode();
     method public void onMeasure(int, int);
     method public void setShowText(boolean);
     method public void setSplitTrack(boolean);
@@ -41500,8 +41702,12 @@
     method public void setThumbDrawable(android.graphics.drawable.Drawable);
     method public void setThumbResource(int);
     method public void setThumbTextPadding(int);
+    method public void setThumbTintList(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
     method public void setTrackDrawable(android.graphics.drawable.Drawable);
     method public void setTrackResource(int);
+    method public void setTrackTintList(android.content.res.ColorStateList);
+    method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 1b69ee8..9322973 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -6,6 +6,14 @@
 
 }
 
+package android.net {
+
+  public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
+    method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
+  }
+
+}
+
 package android.os {
 
   public final class PowerManager {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 2a0ed90..cf608a8 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -134,8 +134,9 @@
                 "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am stack list\n" +
                 "       am stack info <STACK_ID>\n" +
-                "       am lock-task <TASK_ID>\n" +
-                "       am lock-task stop\n" +
+                "       am task lock <TASK_ID>\n" +
+                "       am task lock stop\n" +
+                "       am task resizeable <TASK_ID> [true|false]\n" +
                 "       am get-config\n" +
                 "\n" +
                 "am start: start an Activity.  Options are:\n" +
@@ -250,7 +251,11 @@
                 "\n" +
                 "am stack info: display the information about activity stack <STACK_ID>.\n" +
                 "\n" +
-                "am lock-task: bring <TASK_ID> to the front and don't allow other tasks to run\n" +
+                "am task lock: bring <TASK_ID> to the front and don't allow other tasks to run\n" +
+                "\n" +
+                "am task lock stop: end the current task lock\n" +
+                "\n" +
+                "am task resizeable: change if <TASK_ID> is resizeable (true) or not (false).\n" +
                 "\n" +
                 "am get-config: retrieve the configuration and any recent configurations\n" +
                 "  of the device\n" +
@@ -351,8 +356,8 @@
             runStopUser();
         } else if (op.equals("stack")) {
             runStack();
-        } else if (op.equals("lock-task")) {
-            runLockTask();
+        } else if (op.equals("task")) {
+            runTask();
         } else if (op.equals("get-config")) {
             runGetConfig();
         } else {
@@ -1735,6 +1740,22 @@
         int right = Integer.valueOf(rightStr);
         String bottomStr = nextArgRequired();
         int bottom = Integer.valueOf(bottomStr);
+        if (left < 0) {
+            System.err.println("Error: bad left arg: " + leftStr);
+            return;
+        }
+        if (top < 0) {
+            System.err.println("Error: bad top arg: " + topStr);
+            return;
+        }
+        if (right <= 0) {
+            System.err.println("Error: bad right arg: " + rightStr);
+            return;
+        }
+        if (bottom <= 0) {
+            System.err.println("Error: bad bottom arg: " + bottomStr);
+            return;
+        }
 
         try {
             mAm.resizeStack(stackId, new Rect(left, top, right, bottom));
@@ -1762,7 +1783,19 @@
         }
     }
 
-    private void runLockTask() throws Exception {
+    private void runTask() throws Exception {
+        String op = nextArgRequired();
+        if (op.equals("lock")) {
+            runTaskLock();
+        } else if (op.equals("resizeable")) {
+            runTaskResizeable();
+        } else {
+            showError("Error: unknown command '" + op + "'");
+            return;
+        }
+    }
+
+    private void runTaskLock() throws Exception {
         String taskIdStr = nextArgRequired();
         try {
             if (taskIdStr.equals("stop")) {
@@ -1777,6 +1810,18 @@
         }
     }
 
+    private void runTaskResizeable() throws Exception {
+        final String taskIdStr = nextArgRequired();
+        final int taskId = Integer.valueOf(taskIdStr);
+        final String resizeableStr = nextArgRequired();
+        final boolean resizeable = Boolean.valueOf(resizeableStr);
+
+        try {
+            mAm.setTaskResizeable(taskId, resizeable);
+        } catch (RemoteException e) {
+        }
+    }
+
     private List<Configuration> getRecentConfigurations(int days) {
         IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
                     Context.USAGE_STATS_SERVICE));
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 6550d22..48a34e7 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -16,20 +16,14 @@
 
 #define LOG_TAG "BootAnimation"
 
-#include <cutils/properties.h>
-
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
-
+#include <cutils/properties.h>
+#include <sys/resource.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
 
-#if defined(HAVE_PTHREADS)
-# include <pthread.h>
-# include <sys/resource.h>
-#endif
-
 #include "BootAnimation.h"
 
 using namespace android;
@@ -38,9 +32,7 @@
 
 int main()
 {
-#if defined(HAVE_PTHREADS)
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
-#endif
 
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.nobootanimation", value, "0");
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 27a03b6..5f7a17d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -54,10 +54,6 @@
         int action, in Bundle arguments, int interactionId,
         IAccessibilityInteractionConnectionCallback callback, long threadId);
 
-    boolean computeClickPointInScreen(int accessibilityWindowId, long accessibilityNodeId,
-        int interactionId, IAccessibilityInteractionConnectionCallback callback,
-        long threadId);
-
     AccessibilityWindowInfo getWindow(int windowId);
 
     List<AccessibilityWindowInfo> getWindows();
diff --git a/core/java/android/alsa/AlsaCardsParser.java b/core/java/android/alsa/AlsaCardsParser.java
index 2c7d502..26a61ae 100644
--- a/core/java/android/alsa/AlsaCardsParser.java
+++ b/core/java/android/alsa/AlsaCardsParser.java
@@ -80,8 +80,11 @@
               } else if (lineIndex == 1) {
                   tokenIndex = mTokenizer.nextToken(line, 0);
                   if (tokenIndex != -1) {
-                      mCardDescription = line.substring(tokenIndex);
-                      mIsUsb = mCardDescription.contains(kUsbCardKeyStr);
+                      int keyIndex = line.indexOf(kUsbCardKeyStr);
+                      mIsUsb = keyIndex != -1;
+                      if (mIsUsb) {
+                          mCardDescription = line.substring(tokenIndex, keyIndex - 1);
+                      }
                   }
             }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 23b05c4..8b4b292 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1534,13 +1534,17 @@
      * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current
      * application.  You can override this method to place into the bundle anything
      * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part
-     * of the assist Intent.  The default implementation does nothing.
+     * of the assist Intent.  The default implementation automatically generates a
+     * {@link AssistData} from your activity and places it in to the Bundle; if you
+     * don't want your UI reported to the assistant, don't call this default
+     * implementation.
      *
      * <p>This function will be called after any global assist callbacks that had
      * been registered with {@link Application#registerOnProvideAssistDataListener
      * Application.registerOnProvideAssistDataListener}.
      */
     public void onProvideAssistData(Bundle data) {
+        data.putParcelable(AssistData.ASSIST_KEY, new AssistData(this));
     }
 
     /**
@@ -2373,8 +2377,10 @@
         if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
             return false;
         } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
-            if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
-                    keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
+            Window w = getWindow();
+            if (w.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
+                    w.performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, keyCode, event,
+                            Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
                 return true;
             }
             return false;
@@ -2939,7 +2945,8 @@
      * time it needs to be displayed.
      */
     public void invalidateOptionsMenu() {
-        if (mActionBar == null || !mActionBar.invalidateOptionsMenu()) {
+        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
+                (mActionBar == null || !mActionBar.invalidateOptionsMenu())) {
             mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
         }
     }
@@ -3151,7 +3158,8 @@
      * open, this method does nothing.
      */
     public void openOptionsMenu() {
-        if (mActionBar == null || !mActionBar.openOptionsMenu()) {
+        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
+                (mActionBar == null || !mActionBar.openOptionsMenu())) {
             mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
         }
     }
@@ -3161,7 +3169,9 @@
      * closed, this method does nothing.
      */
     public void closeOptionsMenu() {
-        mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+        }
     }
 
     /**
@@ -3220,7 +3230,9 @@
      * Programmatically closes the most recently opened context menu, if showing.
      */
     public void closeContextMenu() {
-        mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
+        if (mWindow.hasFeature(Window.FEATURE_CONTEXT_MENU)) {
+            mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
+        }
     }
 
     /**
@@ -4607,7 +4619,7 @@
         if (Looper.myLooper() != mMainThread.getLooper()) {
             throw new IllegalStateException("Must be called from main thread");
         }
-        mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
+        mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false);
     }
 
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7a636db..c6ffef6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -256,6 +256,9 @@
     /** @hide User operation call: given user id is the current user, can't be stopped. */
     public static final int USER_OP_IS_CURRENT = -2;
 
+    /** @hide Process does not exist. */
+    public static final int PROCESS_STATE_NONEXISTENT = -1;
+
     /** @hide Process is a persistent system process. */
     public static final int PROCESS_STATE_PERSISTENT = 0;
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 379fe11..47f57ea 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -766,6 +766,14 @@
             return true;
         }
 
+        case GET_FOCUSED_STACK_ID_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int focusedStackId = getFocusedStackId();
+            reply.writeNoException();
+            reply.writeInt(focusedStackId);
+            return true;
+        }
+
         case REGISTER_TASK_STACK_LISTENER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -2292,6 +2300,15 @@
             return true;
         }
 
+        case SET_TASK_RESIZEABLE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            boolean resizeable = (data.readInt() == 1) ? true : false;
+            setTaskResizeable(taskId, resizeable);
+            reply.writeNoException();
+            return true;
+        }
+
         case GET_TASK_DESCRIPTION_ICON_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String filename = data.readString();
@@ -3290,6 +3307,18 @@
         reply.recycle();
     }
     @Override
+    public int getFocusedStackId() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(GET_FOCUSED_STACK_ID_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int focusedStackId = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return focusedStackId;
+    }
+    @Override
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -5382,6 +5411,19 @@
     }
 
     @Override
+    public void setTaskResizeable(int taskId, boolean resizeable) throws  RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        data.writeInt(resizeable ? 1 : 0);
+        mRemote.transact(SET_TASK_RESIZEABLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea37b89..de3c95c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -293,6 +293,7 @@
         boolean hideForNow;
         Configuration newConfig;
         Configuration createdConfig;
+        Configuration overrideConfig;
         ActivityClientRecord nextIdle;
 
         ProfilerInfo profilerInfo;
@@ -615,12 +616,13 @@
 
         // we use token to identify this activity without having to send the
         // activity itself back to the activity manager. (matters more with ipc)
+        @Override
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-                String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
-                PersistableBundle persistentState, List<ResultInfo> pendingResults,
-                List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-                ProfilerInfo profilerInfo) {
+                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
+                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+                int procState, Bundle state, PersistableBundle persistentState,
+                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
 
             updateProcessState(procState, false);
 
@@ -644,16 +646,19 @@
 
             r.profilerInfo = profilerInfo;
 
+            r.overrideConfig = overrideConfig;
             updatePendingConfiguration(curConfig);
 
             sendMessage(H.LAUNCH_ACTIVITY, r);
         }
 
+        @Override
         public final void scheduleRelaunchActivity(IBinder token,
                 List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-                int configChanges, boolean notResumed, Configuration config) {
+                int configChanges, boolean notResumed, Configuration config,
+                Configuration overrideConfig) {
             requestRelaunchActivity(token, pendingResults, pendingNewIntents,
-                    configChanges, notResumed, config, true);
+                    configChanges, notResumed, config, overrideConfig, true);
         }
 
         public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) {
@@ -887,6 +892,7 @@
             sendMessage(H.LOW_MEMORY, null);
         }
 
+        @Override
         public void scheduleActivityConfigurationChanged(IBinder token) {
             sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
         }
@@ -1668,7 +1674,7 @@
             String[] libDirs, int displayId, Configuration overrideConfiguration,
             LoadedApk pkgInfo) {
         return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
-                displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
+                displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
     }
 
     final Handler getHandler() {
@@ -2353,7 +2359,8 @@
 
     private Context createBaseContextForActivity(ActivityClientRecord r,
             final Activity activity) {
-        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
+        ContextImpl appContext =
+                ContextImpl.createActivityContext(this, r.packageInfo, r.overrideConfig);
         appContext.setOuterContext(activity);
         Context baseContext = appContext;
 
@@ -3561,7 +3568,7 @@
 
             // request all activities to relaunch for the changes to take place
             for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
-                requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, false);
+                requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false);
             }
         }
     }
@@ -3805,7 +3812,7 @@
     public final void requestRelaunchActivity(IBinder token,
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
             int configChanges, boolean notResumed, Configuration config,
-            boolean fromServer) {
+            Configuration overrideConfig, boolean fromServer) {
         ActivityClientRecord target = null;
 
         synchronized (mResourcesManager) {
@@ -3854,6 +3861,9 @@
             if (config != null) {
                 target.createdConfig = config;
             }
+            if (overrideConfig != null) {
+                target.overrideConfig = overrideConfig;
+            }
             target.pendingConfigChanges |= configChanges;
         }
     }
@@ -3966,6 +3976,7 @@
             }
         }
         r.startsNotResumed = tmp.startsNotResumed;
+        r.overrideConfig = tmp.overrideConfig;
 
         handleLaunchActivity(r, currentIntent);
     }
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d0d9d71..e3b27b5 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -799,6 +799,15 @@
         mIsStartingTransition = false;
     }
 
+    /**
+     * Cancels any pending transitions and returns true if there is a transition is in
+     * the middle of starting.
+     */
+    protected boolean cancelPendingTransitions() {
+        mPendingTransition = null;
+        return mIsStartingTransition;
+    }
+
     protected void moveSharedElementsToOverlay() {
         if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
             return;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 555d20b..a2bfa4e 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -22,6 +22,7 @@
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.Window;
 
 import java.lang.ref.WeakReference;
@@ -252,7 +253,7 @@
         }
     }
 
-    public boolean startExitBackTransition(Activity activity) {
+    public boolean startExitBackTransition(final Activity activity) {
         if (mEnteringNames == null) {
             return false;
         } else {
@@ -260,10 +261,11 @@
                 mHasExited = true;
                 Transition enterViewsTransition = null;
                 ViewGroup decor = null;
+                boolean delayExitBack = false;
                 if (mEnterTransitionCoordinator != null) {
                     enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
                     decor = mEnterTransitionCoordinator.getDecor();
-                    mEnterTransitionCoordinator.cancelEnter();
+                    delayExitBack = mEnterTransitionCoordinator.cancelEnter();
                     mEnterTransitionCoordinator = null;
                     if (enterViewsTransition != null && decor != null) {
                         enterViewsTransition.pause(decor);
@@ -275,7 +277,23 @@
                 if (enterViewsTransition != null && decor != null) {
                     enterViewsTransition.resume(decor);
                 }
-                mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+                if (delayExitBack && decor != null) {
+                    final ViewGroup finalDecor = decor;
+                    decor.getViewTreeObserver().addOnPreDrawListener(
+                            new ViewTreeObserver.OnPreDrawListener() {
+                                @Override
+                                public boolean onPreDraw() {
+                                    finalDecor.getViewTreeObserver().removeOnPreDrawListener(this);
+                                    if (mReturnExitCoordinator != null) {
+                                        mReturnExitCoordinator.startExit(activity.mResultCode,
+                                                activity.mResultData);
+                                    }
+                                    return true;
+                                }
+                            });
+                } else {
+                    mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+                }
             }
             return true;
         }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index eb3ddb2..349b66d 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -140,6 +140,10 @@
             int ident = data.readInt();
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
             Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
+            Configuration overrideConfig = null;
+            if (data.readInt() != 0) {
+                overrideConfig = Configuration.CREATOR.createFromParcel(data);
+            }
             CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             String referrer = data.readString();
             IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface(
@@ -153,8 +157,8 @@
             boolean isForward = data.readInt() != 0;
             ProfilerInfo profilerInfo = data.readInt() != 0
                     ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
-            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, referrer,
-                    voiceInteractor, procState, state, persistentState, ri, pi,
+            scheduleLaunchActivity(intent, b, ident, info, curConfig, overrideConfig, compatInfo,
+                    referrer, voiceInteractor, procState, state, persistentState, ri, pi,
                     notResumed, isForward, profilerInfo);
             return true;
         }
@@ -167,14 +171,15 @@
             List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR);
             int configChanges = data.readInt();
             boolean notResumed = data.readInt() != 0;
-            Configuration config = null;
+            Configuration config = Configuration.CREATOR.createFromParcel(data);
+            Configuration overrideConfig = null;
             if (data.readInt() != 0) {
-                config = Configuration.CREATOR.createFromParcel(data);
+                overrideConfig = Configuration.CREATOR.createFromParcel(data);
             }
-            scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config);
+            scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig);
             return true;
         }
-        
+
         case SCHEDULE_NEW_INTENT_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -775,11 +780,11 @@
     }
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-            ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
-            PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-            ProfilerInfo profilerInfo) throws RemoteException {
+            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
+            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+            int procState, Bundle state, PersistableBundle persistentState,
+            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         intent.writeToParcel(data, 0);
@@ -787,6 +792,12 @@
         data.writeInt(ident);
         info.writeToParcel(data, 0);
         curConfig.writeToParcel(data, 0);
+        if (overrideConfig != null) {
+            data.writeInt(1);
+            overrideConfig.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
         compatInfo.writeToParcel(data, 0);
         data.writeString(referrer);
         data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
@@ -810,8 +821,8 @@
 
     public final void scheduleRelaunchActivity(IBinder token,
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-            int configChanges, boolean notResumed, Configuration config)
-            throws RemoteException {
+            int configChanges, boolean notResumed, Configuration config,
+            Configuration overrideConfig) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
@@ -819,9 +830,10 @@
         data.writeTypedList(pendingNewIntents);
         data.writeInt(configChanges);
         data.writeInt(notResumed ? 1 : 0);
-        if (config != null) {
+        config.writeToParcel(data, 0);
+        if (overrideConfig != null) {
             data.writeInt(1);
-            config.writeToParcel(data, 0);
+            overrideConfig.writeToParcel(data, 0);
         } else {
             data.writeInt(0);
         }
@@ -1112,8 +1124,7 @@
         data.recycle();
     }
 
-    public final void scheduleActivityConfigurationChanged(
-            IBinder token) throws RemoteException {
+    public final void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
diff --git a/core/java/android/app/AssistData.java b/core/java/android/app/AssistData.java
new file mode 100644
index 0000000..8d3d348
--- /dev/null
+++ b/core/java/android/app/AssistData.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewAssistData;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+import android.widget.Checkable;
+
+import java.util.ArrayList;
+
+/**
+ * Assist data automatically created by the platform's implementation
+ * of {@link Activity#onProvideAssistData}.  Retrieve it from the assist
+ * data with {@link #getAssistData(android.os.Bundle)}.
+ */
+final public class AssistData implements Parcelable {
+    static final String TAG = "AssistData";
+
+    /**
+     * Key name this data structure is stored in the Bundle generated by
+     * {@link Activity#onProvideAssistData}.
+     */
+    public static final String ASSIST_KEY = "android:assist";
+
+    final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>();
+
+    ViewAssistDataImpl mTmpViewAssistDataImpl = new ViewAssistDataImpl();
+    Bundle mTmpExtras = new Bundle();
+
+    final static class ViewAssistDataImpl extends ViewAssistData {
+        CharSequence mText;
+        int mTextSelectionStart = -1;
+        int mTextSelectionEnd = -1;
+        CharSequence mHint;
+
+        @Override
+        public void setText(CharSequence text) {
+            mText = text;
+            mTextSelectionStart = mTextSelectionEnd = -1;
+        }
+
+        @Override
+        public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+            mText = text;
+            mTextSelectionStart = selectionStart;
+            mTextSelectionEnd = selectionEnd;
+        }
+
+        @Override
+        public void setHint(CharSequence hint) {
+            mHint = hint;
+        }
+
+        @Override
+        public CharSequence getText() {
+            return mText;
+        }
+
+        @Override
+        public int getTextSelectionStart() {
+            return mTextSelectionStart;
+        }
+
+        @Override
+        public int getTextSelectionEnd() {
+            return mTextSelectionEnd;
+        }
+
+        @Override
+        public CharSequence getHint() {
+            return mHint;
+        }
+    }
+
+    final static class ViewNodeTextImpl {
+        final String mText;
+        final int mTextSelectionStart;
+        final int mTextSelectionEnd;
+        final String mHint;
+
+        ViewNodeTextImpl(ViewAssistDataImpl data) {
+            mText = data.mText != null ? data.mText.toString() : null;
+            mTextSelectionStart = data.mTextSelectionStart;
+            mTextSelectionEnd = data.mTextSelectionEnd;
+            mHint = data.mHint != null ? data.mHint.toString() : null;
+        }
+
+        ViewNodeTextImpl(Parcel in) {
+            mText = in.readString();
+            mTextSelectionStart = in.readInt();
+            mTextSelectionEnd = in.readInt();
+            mHint = in.readString();
+        }
+
+        void writeToParcel(Parcel out) {
+            out.writeString(mText);
+            out.writeInt(mTextSelectionStart);
+            out.writeInt(mTextSelectionEnd);
+            out.writeString(mHint);
+        }
+    }
+
+    final static class ViewNodeImpl {
+        final int mX;
+        final int mY;
+        final int mScrollX;
+        final int mScrollY;
+        final int mWidth;
+        final int mHeight;
+
+        static final int FLAGS_DISABLED = 0x00000001;
+        static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
+        static final int FLAGS_FOCUSABLE = 0x00000010;
+        static final int FLAGS_FOCUSED = 0x00000020;
+        static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000;
+        static final int FLAGS_SELECTED = 0x00000040;
+        static final int FLAGS_ACTIVATED = 0x40000000;
+        static final int FLAGS_CHECKABLE = 0x00000100;
+        static final int FLAGS_CHECKED = 0x00000200;
+        static final int FLAGS_CLICKABLE = 0x00004000;
+        static final int FLAGS_LONG_CLICKABLE = 0x00200000;
+
+        final int mFlags;
+
+        final String mClassName;
+        final String mContentDescription;
+
+        final ViewNodeTextImpl mText;
+        final Bundle mExtras;
+
+        final ViewNodeImpl[] mChildren;
+
+        ViewNodeImpl(AssistData assistData, View view, int left, int top,
+                CharSequence contentDescription) {
+            mX = left;
+            mY = top;
+            mScrollX = view.getScrollX();
+            mScrollY = view.getScrollY();
+            mWidth = view.getWidth();
+            mHeight = view.getHeight();
+            int flags = view.getVisibility();
+            if (!view.isEnabled()) {
+                flags |= FLAGS_DISABLED;
+            }
+            if (!view.isClickable()) {
+                flags |= FLAGS_CLICKABLE;
+            }
+            if (!view.isFocusable()) {
+                flags |= FLAGS_FOCUSABLE;
+            }
+            if (!view.isFocused()) {
+                flags |= FLAGS_FOCUSED;
+            }
+            if (!view.isAccessibilityFocused()) {
+                flags |= FLAGS_ACCESSIBILITY_FOCUSED;
+            }
+            if (!view.isSelected()) {
+                flags |= FLAGS_SELECTED;
+            }
+            if (!view.isActivated()) {
+                flags |= FLAGS_ACTIVATED;
+            }
+            if (!view.isLongClickable()) {
+                flags |= FLAGS_LONG_CLICKABLE;
+            }
+            if (view instanceof Checkable) {
+                flags |= FLAGS_CHECKABLE;
+                if (((Checkable)view).isChecked()) {
+                    flags |= FLAGS_CHECKED;
+                }
+            }
+            mFlags = flags;
+            mClassName = view.getAccessibilityClassName().toString();
+            mContentDescription = contentDescription != null ? contentDescription.toString() : null;
+            final ViewAssistDataImpl viewData = assistData.mTmpViewAssistDataImpl;
+            final Bundle extras = assistData.mTmpExtras;
+            view.onProvideAssistData(viewData, extras);
+            if (viewData.mText != null || viewData.mHint != null) {
+                mText = new ViewNodeTextImpl(viewData);
+                assistData.mTmpViewAssistDataImpl = new ViewAssistDataImpl();
+            } else {
+                mText = null;
+            }
+            if (!extras.isEmpty()) {
+                mExtras = extras;
+                assistData.mTmpExtras = new Bundle();
+            } else {
+                mExtras = null;
+            }
+            if (view instanceof ViewGroup) {
+                ViewGroup vg = (ViewGroup)view;
+                final int NCHILDREN = vg.getChildCount();
+                if (NCHILDREN > 0) {
+                    mChildren = new ViewNodeImpl[NCHILDREN];
+                    for (int i=0; i<NCHILDREN; i++) {
+                        mChildren[i] = new ViewNodeImpl(assistData, vg.getChildAt(i));
+                    }
+                } else {
+                    mChildren = null;
+                }
+            } else {
+                mChildren = null;
+            }
+        }
+
+        ViewNodeImpl(AssistData assistData, View view) {
+            this(assistData, view, view.getLeft(), view.getTop(), view.getContentDescription());
+        }
+
+        ViewNodeImpl(Parcel in) {
+            mX = in.readInt();
+            mY = in.readInt();
+            mScrollX = in.readInt();
+            mScrollY = in.readInt();
+            mWidth = in.readInt();
+            mHeight = in.readInt();
+            mFlags = in.readInt();
+            mClassName = in.readString();
+            mContentDescription = in.readString();
+            if (in.readInt() != 0) {
+                mText = new ViewNodeTextImpl(in);
+            } else {
+                mText = null;
+            }
+            mExtras = in.readBundle();
+            final int NCHILDREN = in.readInt();
+            if (NCHILDREN > 0) {
+                mChildren = new ViewNodeImpl[NCHILDREN];
+                for (int i=0; i<NCHILDREN; i++) {
+                    mChildren[i] = new ViewNodeImpl(in);
+                }
+            } else {
+                mChildren = null;
+            }
+        }
+
+        void writeToParcel(Parcel out) {
+            out.writeInt(mX);
+            out.writeInt(mY);
+            out.writeInt(mScrollX);
+            out.writeInt(mScrollY);
+            out.writeInt(mWidth);
+            out.writeInt(mHeight);
+            out.writeInt(mFlags);
+            out.writeString(mClassName);
+            out.writeString(mContentDescription);
+            if (mText != null) {
+                out.writeInt(1);
+                mText.writeToParcel(out);
+            } else {
+                out.writeInt(0);
+            }
+            out.writeBundle(mExtras);
+            if (mChildren != null) {
+                final int NCHILDREN = mChildren.length;
+                out.writeInt(NCHILDREN);
+                for (int i=0; i<NCHILDREN; i++) {
+                    mChildren[i].writeToParcel(out);
+                }
+            } else {
+                out.writeInt(0);
+            }
+        }
+    }
+
+    /**
+     * Provides access to information about a single view in the assist data.
+     */
+    static public class ViewNode {
+        ViewNodeImpl mImpl;
+
+        public ViewNode() {
+        }
+
+        public int getLeft() {
+            return mImpl.mX;
+        }
+
+        public int getTop() {
+            return mImpl.mY;
+        }
+
+        public int getScrollX() {
+            return mImpl.mScrollX;
+        }
+
+        public int getScrollY() {
+            return mImpl.mScrollY;
+        }
+
+        public int getWidth() {
+            return mImpl.mWidth;
+        }
+
+        public int getHeight() {
+            return mImpl.mHeight;
+        }
+
+        public int getVisibility() {
+            return mImpl.mFlags&ViewNodeImpl.FLAGS_VISIBILITY_MASK;
+        }
+
+        public boolean isEnabled() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_DISABLED) == 0;
+        }
+
+        public boolean isClickable() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_CLICKABLE) != 0;
+        }
+
+        public boolean isFocusable() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSABLE) != 0;
+        }
+
+        public boolean isFocused() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSED) != 0;
+        }
+
+        public boolean isAccessibilityFocused() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
+        }
+
+        public boolean isCheckable() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKABLE) != 0;
+        }
+
+        public boolean isChecked() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKED) != 0;
+        }
+
+        public boolean isSelected() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_SELECTED) != 0;
+        }
+
+        public boolean isActivated() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACTIVATED) != 0;
+        }
+
+        public boolean isLongClickable() {
+            return (mImpl.mFlags&ViewNodeImpl.FLAGS_LONG_CLICKABLE) != 0;
+        }
+
+        public String getClassName() {
+            return mImpl.mClassName;
+        }
+
+        public String getContentDescription() {
+            return mImpl.mContentDescription;
+        }
+
+        public String getText() {
+            return mImpl.mText != null ? mImpl.mText.mText : null;
+        }
+
+        public int getTextSelectionStart() {
+            return mImpl.mText != null ? mImpl.mText.mTextSelectionStart : -1;
+        }
+
+        public int getTextSelectionEnd() {
+            return mImpl.mText != null ? mImpl.mText.mTextSelectionEnd : -1;
+        }
+
+        public String getHint() {
+            return mImpl.mText != null ? mImpl.mText.mHint : null;
+        }
+
+        public Bundle getExtras() {
+            return mImpl.mExtras;
+        }
+
+        public int getChildCount() {
+            return mImpl.mChildren != null ? mImpl.mChildren.length : 0;
+        }
+
+        public void getChildAt(int index, ViewNode outNode) {
+            outNode.mImpl = mImpl.mChildren[index];
+        }
+    }
+
+    AssistData(Activity activity) {
+        ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
+                activity.getActivityToken());
+        for (int i=0; i<views.size(); i++) {
+            ViewRootImpl root = views.get(i);
+            View view = root.getView();
+            Rect rect = new Rect();
+            view.getBoundsOnScreen(rect);
+            CharSequence title = root.getTitle();
+            mRootViews.add(new ViewNodeImpl(this, view, rect.left, rect.top,
+                    title != null ? title : view.getContentDescription()));
+        }
+    }
+
+    AssistData(Parcel in) {
+        final int N = in.readInt();
+        for (int i=0; i<N; i++) {
+            mRootViews.add(new ViewNodeImpl(in));
+        }
+        //dump();
+    }
+
+    void dump() {
+        ViewNode node = new ViewNode();
+        final int N = getWindowCount();
+        for (int i=0; i<N; i++) {
+            Log.i(TAG, "Window #" + i + ":");
+            getWindowAt(i, node);
+            dump("  ", node);
+        }
+    }
+
+    void dump(String prefix, ViewNode node) {
+        Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
+                + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
+        int scrollX = node.getScrollX();
+        int scrollY = node.getScrollY();
+        if (scrollX != 0 || scrollY != 0) {
+            Log.i(TAG, prefix + "  Scroll: " + scrollX + "," + scrollY);
+        }
+        String contentDescription = node.getContentDescription();
+        if (contentDescription != null) {
+            Log.i(TAG, prefix + "  Content description: " + contentDescription);
+        }
+        String text = node.getText();
+        if (text != null) {
+            Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-"
+                    + node.getTextSelectionEnd() + "): " + text);
+        }
+        String hint = node.getHint();
+        if (hint != null) {
+            Log.i(TAG, prefix + "  Hint: " + hint);
+        }
+        Bundle extras = node.getExtras();
+        if (extras != null) {
+            Log.i(TAG, prefix + "  Extras: " + extras);
+        }
+        final int NCHILDREN = node.getChildCount();
+        if (NCHILDREN > 0) {
+            Log.i(TAG, prefix + "  Children:");
+            String cprefix = prefix + "    ";
+            ViewNode cnode = new ViewNode();
+            for (int i=0; i<NCHILDREN; i++) {
+                node.getChildAt(i, cnode);
+                dump(cprefix, cnode);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the framework-generated AssistData that is stored within
+     * the Bundle filled in by {@link Activity#onProvideAssistData}.
+     */
+    public static AssistData getAssistData(Bundle assistBundle) {
+        return assistBundle.getParcelable(ASSIST_KEY);
+    }
+
+    /**
+     * Return the number of window contents that have been collected in this assist data.
+     */
+    public int getWindowCount() {
+        return mRootViews.size();
+    }
+
+    /**
+     * Return the root view for one of the windows in the assist data.
+     * @param index Which window to retrieve, may be 0 to {@link #getWindowCount()}-1.
+     * @param outNode Node in which to place the window's root view.
+     */
+    public void getWindowAt(int index, ViewNode outNode) {
+        outNode.mImpl = mRootViews.get(index);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        int start = out.dataPosition();
+        final int N = mRootViews.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            mRootViews.get(i).writeToParcel(out);
+        }
+        Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes");
+    }
+
+    public static final Parcelable.Creator<AssistData> CREATOR
+            = new Parcelable.Creator<AssistData>() {
+        public AssistData createFromParcel(Parcel in) {
+            return new AssistData(in);
+        }
+
+        public AssistData[] newArray(int size) {
+            return new AssistData[size];
+        }
+    };
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e9df8c6..39caf0b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2264,11 +2264,10 @@
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
-            LoadedApk packageInfo, IBinder activityToken) {
+            LoadedApk packageInfo, Configuration overrideConfiguration) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        if (activityToken == null) throw new IllegalArgumentException("activityInfo");
-        return new ContextImpl(null, mainThread,
-                packageInfo, activityToken, null, false, null, null);
+        return new ContextImpl(null, mainThread, packageInfo, null, null, false, null,
+                overrideConfiguration);
     }
 
     private ContextImpl(ContextImpl container, ActivityThread mainThread,
@@ -2303,15 +2302,14 @@
 
         Resources resources = packageInfo.getResources(mainThread);
         if (resources != null) {
-            if (activityToken != null
-                    || displayId != Display.DEFAULT_DISPLAY
+            if (displayId != Display.DEFAULT_DISPLAY
                     || overrideConfiguration != null
                     || (compatInfo != null && compatInfo.applicationScale
                             != resources.getCompatibilityInfo().applicationScale)) {
                 resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
                         packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
                         packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
-                        overrideConfiguration, compatInfo, activityToken);
+                        overrideConfiguration, compatInfo);
             }
         }
         mResources = resources;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 12d4513..067073a 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -910,21 +910,27 @@
      * @see Activity#openOptionsMenu()
      */
     public void openOptionsMenu() {
-        mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+        }
     }
-    
+
     /**
      * @see Activity#closeOptionsMenu()
      */
     public void closeOptionsMenu() {
-        mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+        }
     }
 
     /**
      * @see Activity#invalidateOptionsMenu()
      */
     public void invalidateOptionsMenu() {
-        mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+            mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+        }
     }
 
     /**
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index ecf19c7..c053c83 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,7 +18,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.graphics.Matrix;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -565,7 +564,12 @@
         clearState();
     }
 
-    public void cancelEnter() {
+    /**
+     * Cancels the enter transition.
+     * @return True if the enter transition is still pending capturing the target state. If so,
+     * any transition started on the decor will do nothing.
+     */
+    public boolean cancelEnter() {
         setGhostVisibility(View.INVISIBLE);
         mHasStopped = true;
         mIsCanceled = true;
@@ -576,6 +580,7 @@
         }
         mActivity = null;
         clearState();
+        return super.cancelPendingTransitions();
     }
 
     private void makeOpaque() {
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 52884f7..ff1175f 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -32,6 +32,7 @@
     void checkEmbeddedAllowed(in Intent intent);
     void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender);
     int getDisplayId();
+    int getStackId();
     boolean injectEvent(in InputEvent event);
     void release();
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f152c6f..467c99a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -139,6 +139,7 @@
     public StackInfo getStackInfo(int stackId) throws RemoteException;
     public boolean isInHomeStack(int taskId) throws RemoteException;
     public void setFocusedStack(int stackId) throws RemoteException;
+    public int getFocusedStackId() throws RemoteException;
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
@@ -459,6 +460,7 @@
 
     public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
             throws RemoteException;
+    public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
 
     public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts)
@@ -800,4 +802,6 @@
     // Start of M transactions
     int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+280;
     int CREATE_STACK_ON_DISPLAY = IBinder.FIRST_CALL_TRANSACTION+281;
+    int GET_FOCUSED_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+282;
+    int SET_TASK_RESIZEABLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+283;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8bf8cd7..f2c912e 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -33,7 +33,6 @@
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.IInterface;
-import android.service.voice.IVoiceInteractionSession;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 
@@ -59,14 +58,14 @@
             throws RemoteException;
     void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-            ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
-            PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-            ProfilerInfo profilerInfo) throws RemoteException;
+            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
+            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+            int procState, Bundle state, PersistableBundle persistentState,
+            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException;
     void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, int configChanges,
-            boolean notResumed, Configuration config) throws RemoteException;
+            List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
+            Configuration config, Configuration overrideConfig) throws RemoteException;
     void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException;
     void scheduleDestroyActivity(IBinder token, boolean finished,
             int configChanges) throws RemoteException;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 860b9cc..87e744c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5065,6 +5065,403 @@
     }
 
     /**
+     * <p>Helper class to add Android Auto extensions to notifications. To create a notification
+     * with car extensions:
+     *
+     * <ol>
+     *  <li>Create an {@link Notification.Builder}, setting any desired
+     *  properties.
+     *  <li>Create a {@link CarExtender}.
+     *  <li>Set car-specific properties using the {@code add} and {@code set} methods of
+     *  {@link CarExtender}.
+     *  <li>Call {@link Notification.Builder#extend(Notification.Extender)}
+     *  to apply the extensions to a notification.
+     * </ol>
+     *
+     * <pre class="prettyprint">
+     * Notification notification = new Notification.Builder(context)
+     *         ...
+     *         .extend(new CarExtender()
+     *                 .set*(...))
+     *         .build();
+     * </pre>
+     *
+     * <p>Car extensions can be accessed on an existing notification by using the
+     * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
+     * to access values.
+     */
+    public static final class CarExtender implements Extender {
+        private static final String TAG = "CarExtender";
+
+        private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
+        private static final String EXTRA_LARGE_ICON = "large_icon";
+        private static final String EXTRA_CONVERSATION = "car_conversation";
+        private static final String EXTRA_COLOR = "app_color";
+
+        private Bitmap mLargeIcon;
+        private UnreadConversation mUnreadConversation;
+        private int mColor = Notification.COLOR_DEFAULT;
+
+        /**
+         * Create a {@link CarExtender} with default options.
+         */
+        public CarExtender() {
+        }
+
+        /**
+         * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
+         *
+         * @param notif The notification from which to copy options.
+         */
+        public CarExtender(Notification notif) {
+            Bundle carBundle = notif.extras == null ?
+                    null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
+            if (carBundle != null) {
+                mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
+                mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
+
+                Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
+                mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
+            }
+        }
+
+        /**
+         * Apply car extensions to a notification that is being built. This is typically called by
+         * the {@link Notification.Builder#extend(Notification.Extender)}
+         * method of {@link Notification.Builder}.
+         */
+        @Override
+        public Notification.Builder extend(Notification.Builder builder) {
+            Bundle carExtensions = new Bundle();
+
+            if (mLargeIcon != null) {
+                carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
+            }
+            if (mColor != Notification.COLOR_DEFAULT) {
+                carExtensions.putInt(EXTRA_COLOR, mColor);
+            }
+
+            if (mUnreadConversation != null) {
+                Bundle b = mUnreadConversation.getBundleForUnreadConversation();
+                carExtensions.putBundle(EXTRA_CONVERSATION, b);
+            }
+
+            builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
+            return builder;
+        }
+
+        /**
+         * Sets the accent color to use when Android Auto presents the notification.
+         *
+         * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
+         * to accent the displayed notification. However, not all colors are acceptable in an
+         * automotive setting. This method can be used to override the color provided in the
+         * notification in such a situation.
+         */
+        public CarExtender setColor(int color) {
+            mColor = color;
+            return this;
+        }
+
+        /**
+         * Gets the accent color.
+         *
+         * @see setColor
+         */
+        public int getColor() {
+            return mColor;
+        }
+
+        /**
+         * Sets the large icon of the car notification.
+         *
+         * If no large icon is set in the extender, Android Auto will display the icon
+         * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
+         *
+         * @param largeIcon The large icon to use in the car notification.
+         * @return This object for method chaining.
+         */
+        public CarExtender setLargeIcon(Bitmap largeIcon) {
+            mLargeIcon = largeIcon;
+            return this;
+        }
+
+        /**
+         * Gets the large icon used in this car notification, or null if no icon has been set.
+         *
+         * @return The large icon for the car notification.
+         * @see CarExtender#setLargeIcon
+         */
+        public Bitmap getLargeIcon() {
+            return mLargeIcon;
+        }
+
+        /**
+         * Sets the unread conversation in a message notification.
+         *
+         * @param unreadConversation The unread part of the conversation this notification conveys.
+         * @return This object for method chaining.
+         */
+        public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
+            mUnreadConversation = unreadConversation;
+            return this;
+        }
+
+        /**
+         * Returns the unread conversation conveyed by this notification.
+         * @see #setUnreadConversation(UnreadConversation)
+         */
+        public UnreadConversation getUnreadConversation() {
+            return mUnreadConversation;
+        }
+
+        /**
+         * A class which holds the unread messages from a conversation.
+         */
+        public static class UnreadConversation {
+            private static final String KEY_AUTHOR = "author";
+            private static final String KEY_TEXT = "text";
+            private static final String KEY_MESSAGES = "messages";
+            private static final String KEY_REMOTE_INPUT = "remote_input";
+            private static final String KEY_ON_REPLY = "on_reply";
+            private static final String KEY_ON_READ = "on_read";
+            private static final String KEY_PARTICIPANTS = "participants";
+            private static final String KEY_TIMESTAMP = "timestamp";
+
+            private final String[] mMessages;
+            private final RemoteInput mRemoteInput;
+            private final PendingIntent mReplyPendingIntent;
+            private final PendingIntent mReadPendingIntent;
+            private final String[] mParticipants;
+            private final long mLatestTimestamp;
+
+            UnreadConversation(String[] messages, RemoteInput remoteInput,
+                    PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
+                    String[] participants, long latestTimestamp) {
+                mMessages = messages;
+                mRemoteInput = remoteInput;
+                mReadPendingIntent = readPendingIntent;
+                mReplyPendingIntent = replyPendingIntent;
+                mParticipants = participants;
+                mLatestTimestamp = latestTimestamp;
+            }
+
+            /**
+             * Gets the list of messages conveyed by this notification.
+             */
+            public String[] getMessages() {
+                return mMessages;
+            }
+
+            /**
+             * Gets the remote input that will be used to convey the response to a message list, or
+             * null if no such remote input exists.
+             */
+            public RemoteInput getRemoteInput() {
+                return mRemoteInput;
+            }
+
+            /**
+             * Gets the pending intent that will be triggered when the user replies to this
+             * notification.
+             */
+            public PendingIntent getReplyPendingIntent() {
+                return mReplyPendingIntent;
+            }
+
+            /**
+             * Gets the pending intent that Android Auto will send after it reads aloud all messages
+             * in this object's message list.
+             */
+            public PendingIntent getReadPendingIntent() {
+                return mReadPendingIntent;
+            }
+
+            /**
+             * Gets the participants in the conversation.
+             */
+            public String[] getParticipants() {
+                return mParticipants;
+            }
+
+            /**
+             * Gets the firs participant in the conversation.
+             */
+            public String getParticipant() {
+                return mParticipants.length > 0 ? mParticipants[0] : null;
+            }
+
+            /**
+             * Gets the timestamp of the conversation.
+             */
+            public long getLatestTimestamp() {
+                return mLatestTimestamp;
+            }
+
+            Bundle getBundleForUnreadConversation() {
+                Bundle b = new Bundle();
+                String author = null;
+                if (mParticipants != null && mParticipants.length > 1) {
+                    author = mParticipants[0];
+                }
+                Parcelable[] messages = new Parcelable[mMessages.length];
+                for (int i = 0; i < messages.length; i++) {
+                    Bundle m = new Bundle();
+                    m.putString(KEY_TEXT, mMessages[i]);
+                    m.putString(KEY_AUTHOR, author);
+                    messages[i] = m;
+                }
+                b.putParcelableArray(KEY_MESSAGES, messages);
+                if (mRemoteInput != null) {
+                    b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
+                }
+                b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
+                b.putParcelable(KEY_ON_READ, mReadPendingIntent);
+                b.putStringArray(KEY_PARTICIPANTS, mParticipants);
+                b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
+                return b;
+            }
+
+            static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
+                if (b == null) {
+                    return null;
+                }
+                Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
+                String[] messages = null;
+                if (parcelableMessages != null) {
+                    String[] tmp = new String[parcelableMessages.length];
+                    boolean success = true;
+                    for (int i = 0; i < tmp.length; i++) {
+                        if (!(parcelableMessages[i] instanceof Bundle)) {
+                            success = false;
+                            break;
+                        }
+                        tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
+                        if (tmp[i] == null) {
+                            success = false;
+                            break;
+                        }
+                    }
+                    if (success) {
+                        messages = tmp;
+                    } else {
+                        return null;
+                    }
+                }
+
+                PendingIntent onRead = b.getParcelable(KEY_ON_READ);
+                PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
+
+                RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
+
+                String[] participants = b.getStringArray(KEY_PARTICIPANTS);
+                if (participants == null || participants.length != 1) {
+                    return null;
+                }
+
+                return new UnreadConversation(messages,
+                        remoteInput,
+                        onReply,
+                        onRead,
+                        participants, b.getLong(KEY_TIMESTAMP));
+            }
+        };
+
+        /**
+         * Builder class for {@link CarExtender.UnreadConversation} objects.
+         */
+        public static class Builder {
+            private final List<String> mMessages = new ArrayList<String>();
+            private final String mParticipant;
+            private RemoteInput mRemoteInput;
+            private PendingIntent mReadPendingIntent;
+            private PendingIntent mReplyPendingIntent;
+            private long mLatestTimestamp;
+
+            /**
+             * Constructs a new builder for {@link CarExtender.UnreadConversation}.
+             *
+             * @param name The name of the other participant in the conversation.
+             */
+            public Builder(String name) {
+                mParticipant = name;
+            }
+
+            /**
+             * Appends a new unread message to the list of messages for this conversation.
+             *
+             * The messages should be added from oldest to newest.
+             *
+             * @param message The text of the new unread message.
+             * @return This object for method chaining.
+             */
+            public Builder addMessage(String message) {
+                mMessages.add(message);
+                return this;
+            }
+
+            /**
+             * Sets the pending intent and remote input which will convey the reply to this
+             * notification.
+             *
+             * @param pendingIntent The pending intent which will be triggered on a reply.
+             * @param remoteInput The remote input parcelable which will carry the reply.
+             * @return This object for method chaining.
+             *
+             * @see CarExtender.UnreadConversation#getRemoteInput
+             * @see CarExtender.UnreadConversation#getReplyPendingIntent
+             */
+            public Builder setReplyAction(
+                    PendingIntent pendingIntent, RemoteInput remoteInput) {
+                mRemoteInput = remoteInput;
+                mReplyPendingIntent = pendingIntent;
+
+                return this;
+            }
+
+            /**
+             * Sets the pending intent that will be sent once the messages in this notification
+             * are read.
+             *
+             * @param pendingIntent The pending intent to use.
+             * @return This object for method chaining.
+             */
+            public Builder setReadPendingIntent(PendingIntent pendingIntent) {
+                mReadPendingIntent = pendingIntent;
+                return this;
+            }
+
+            /**
+             * Sets the timestamp of the most recent message in an unread conversation.
+             *
+             * If a messaging notification has been posted by your application and has not
+             * yet been cancelled, posting a later notification with the same id and tag
+             * but without a newer timestamp may result in Android Auto not displaying a
+             * heads up notification for the later notification.
+             *
+             * @param timestamp The timestamp of the most recent message in the conversation.
+             * @return This object for method chaining.
+             */
+            public Builder setLatestTimestamp(long timestamp) {
+                mLatestTimestamp = timestamp;
+                return this;
+            }
+
+            /**
+             * Builds a new unread conversation object.
+             *
+             * @return The new unread conversation object.
+             */
+            public UnreadConversation build() {
+                String[] messages = mMessages.toArray(new String[mMessages.size()]);
+                String[] participants = { mParticipant };
+                return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
+                        mReadPendingIntent, participants, mLatestTimestamp);
+            }
+        }
+    }
+
+    /**
      * Get an array of Notification objects from a parcelable array bundle field.
      * Update the bundle to have a typed array so fetches in the future don't need
      * to do an array copy.
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 1691d8e..fac40b2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -25,7 +25,6 @@
 import android.content.res.Resources;
 import android.content.res.ResourcesKey;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Slog;
@@ -38,8 +37,7 @@
 /** @hide */
 public class ResourcesManager {
     static final String TAG = "ResourcesManager";
-    static final boolean DEBUG_CACHE = false;
-    static final boolean DEBUG_STATS = true;
+    private static final boolean DEBUG = false;
 
     private static ResourcesManager sResourcesManager;
     final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources
@@ -51,7 +49,6 @@
     CompatibilityInfo mResCompatibilityInfo;
 
     Configuration mResConfiguration;
-    final Configuration mTmpConfig = new Configuration();
 
     public static ResourcesManager getInstance() {
         synchronized (ResourcesManager.class) {
@@ -144,30 +141,32 @@
      * Creates the top level Resources for applications with the given compatibility info.
      *
      * @param resDir the resource directory.
+     * @param splitResDirs split resource directories.
      * @param overlayDirs the resource overlay directories.
      * @param libDirs the shared library resource dirs this app references.
-     * @param compatInfo the compability info. Must not be null.
-     * @param token the application token for determining stack bounds.
+     * @param displayId display Id.
+     * @param overrideConfiguration override configurations.
+     * @param compatInfo the compatibility info. Must not be null.
      */
     public Resources getTopLevelResources(String resDir, String[] splitResDirs,
             String[] overlayDirs, String[] libDirs, int displayId,
-            Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
+            Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
         final float scale = compatInfo.applicationScale;
-        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token);
+        Configuration overrideConfigCopy = (overrideConfiguration != null)
+                ? new Configuration(overrideConfiguration) : null;
+        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
         Resources r;
         synchronized (this) {
             // Resources is app scale dependent.
-            if (false) {
-                Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
-            }
+            if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
+
             WeakReference<Resources> wr = mActiveResources.get(key);
             r = wr != null ? wr.get() : null;
             //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
             if (r != null && r.getAssets().isUpToDate()) {
-                if (false) {
-                    Slog.w(TAG, "Returning cached resources " + r + " " + resDir
-                            + ": appScale=" + r.getCompatibilityInfo().applicationScale);
-                }
+                if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+                        + ": appScale=" + r.getCompatibilityInfo().applicationScale
+                        + " key=" + key + " overrideConfig=" + overrideConfiguration);
                 return r;
             }
         }
@@ -213,7 +212,7 @@
         //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
         DisplayMetrics dm = getDisplayMetricsLocked(displayId);
         Configuration config;
-        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+        final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
         final boolean hasOverrideConfig = key.hasOverrideConfiguration();
         if (!isDefaultDisplay || hasOverrideConfig) {
             config = new Configuration(getConfiguration());
@@ -222,16 +221,14 @@
             }
             if (hasOverrideConfig) {
                 config.updateFrom(key.mOverrideConfiguration);
+                if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
             }
         } else {
             config = getConfiguration();
         }
-        r = new Resources(assets, dm, config, compatInfo, token);
-        if (false) {
-            Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
-                    + r.getConfiguration() + " appScale="
-                    + r.getCompatibilityInfo().applicationScale);
-        }
+        r = new Resources(assets, dm, config, compatInfo);
+        if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+                + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
 
         synchronized (this) {
             WeakReference<Resources> wr = mActiveResources.get(key);
@@ -244,7 +241,8 @@
             }
 
             // XXX need to remove entries when weak references go away
-            mActiveResources.put(key, new WeakReference<Resources>(r));
+            mActiveResources.put(key, new WeakReference<>(r));
+            if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
             return r;
         }
     }
@@ -255,7 +253,7 @@
             mResConfiguration = new Configuration();
         }
         if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
-            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+            if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
                     + mResConfiguration.seq + ", newSeq=" + config.seq);
             return false;
         }
@@ -287,7 +285,7 @@
             ResourcesKey key = mActiveResources.keyAt(i);
             Resources r = mActiveResources.valueAt(i).get();
             if (r != null) {
-                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
                         + r + " config to: " + config);
                 int displayId = key.mDisplayId;
                 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 4427ce1..e617553 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.Nullable;
 import android.content.SharedPreferences;
 import android.os.FileUtils;
 import android.os.Looper;
@@ -217,7 +218,8 @@
         }
     }
 
-    public String getString(String key, String defValue) {
+    @Nullable
+    public String getString(String key, @Nullable String defValue) {
         synchronized (this) {
             awaitLoadedLocked();
             String v = (String)mMap.get(key);
@@ -225,7 +227,8 @@
         }
     }
 
-    public Set<String> getStringSet(String key, Set<String> defValues) {
+    @Nullable
+    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
         synchronized (this) {
             awaitLoadedLocked();
             Set<String> v = (Set<String>) mMap.get(key);
@@ -303,13 +306,13 @@
         private final Map<String, Object> mModified = Maps.newHashMap();
         private boolean mClear = false;
 
-        public Editor putString(String key, String value) {
+        public Editor putString(String key, @Nullable String value) {
             synchronized (this) {
                 mModified.put(key, value);
                 return this;
             }
         }
-        public Editor putStringSet(String key, Set<String> values) {
+        public Editor putStringSet(String key, @Nullable Set<String> values) {
             synchronized (this) {
                 mModified.put(key,
                         (values == null) ? null : new HashSet<String>(values));
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8bfe6d3..90d84ee 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -994,6 +994,47 @@
     }
 
     /**
+     * Clear the wallpaper.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void clearWallpaper() {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return;
+        }
+        try {
+            sGlobals.mService.clearWallpaper();
+        } catch (RemoteException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Set the live wallpaper.
+     *
+     * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
+     * permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean setWallpaperComponent(ComponentName name) {
+        if (sGlobals.mService == null) {
+            Log.w(TAG, "WallpaperService not running");
+            return false;
+        }
+        try {
+            sGlobals.mService.setWallpaperComponent(name);
+            return true;
+        } catch (RemoteException e) {
+            // Ignore
+        }
+        return false;
+    }
+
+    /**
      * Set the position of the current wallpaper within any larger space, when
      * that wallpaper is visible behind the given window.  The X and Y offsets
      * are floating point numbers ranging from 0 to 1, representing where the
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55c3960..af48909 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -332,6 +332,16 @@
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
 
     /**
+     * A boolean extra indicating whether device encryption is required as part of Device Owner
+     * provisioning.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION =
+             "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
+
+    /**
      * This MIME type is used for starting the Device Owner provisioning.
      *
      * <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -361,7 +371,8 @@
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li></ul>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li></ul>
      *
      * <p> When device owner provisioning has completed, an intent of the type
      * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
@@ -694,7 +705,7 @@
     public void setPasswordQuality(ComponentName admin, int quality) {
         if (mService != null) {
             try {
-                mService.setPasswordQuality(admin, quality, UserHandle.myUserId());
+                mService.setPasswordQuality(admin, quality);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -747,7 +758,7 @@
     public void setPasswordMinimumLength(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumLength(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumLength(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -801,7 +812,7 @@
     public void setPasswordMinimumUpperCase(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumUpperCase(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumUpperCase(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -862,7 +873,7 @@
     public void setPasswordMinimumLowerCase(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumLowerCase(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumLowerCase(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -922,7 +933,7 @@
     public void setPasswordMinimumLetters(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumLetters(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumLetters(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -980,7 +991,7 @@
     public void setPasswordMinimumNumeric(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumNumeric(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumNumeric(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1039,7 +1050,7 @@
     public void setPasswordMinimumSymbols(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumSymbols(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumSymbols(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1097,7 +1108,7 @@
     public void setPasswordMinimumNonLetter(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordMinimumNonLetter(admin, length, UserHandle.myUserId());
+                mService.setPasswordMinimumNonLetter(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1157,7 +1168,7 @@
     public void setPasswordHistoryLength(ComponentName admin, int length) {
         if (mService != null) {
             try {
-                mService.setPasswordHistoryLength(admin, length, UserHandle.myUserId());
+                mService.setPasswordHistoryLength(admin, length);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1189,7 +1200,7 @@
     public void setPasswordExpirationTimeout(ComponentName admin, long timeout) {
         if (mService != null) {
             try {
-                mService.setPasswordExpirationTimeout(admin, timeout, UserHandle.myUserId());
+                mService.setPasswordExpirationTimeout(admin, timeout);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1334,7 +1345,7 @@
     public void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) {
         if (mService != null) {
             try {
-                mService.setMaximumFailedPasswordsForWipe(admin, num, UserHandle.myUserId());
+                mService.setMaximumFailedPasswordsForWipe(admin, num);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1418,7 +1429,7 @@
     public boolean resetPassword(String password, int flags) {
         if (mService != null) {
             try {
-                return mService.resetPassword(password, flags, UserHandle.myUserId());
+                return mService.resetPassword(password, flags);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1442,7 +1453,7 @@
     public void setMaximumTimeToLock(ComponentName admin, long timeMs) {
         if (mService != null) {
             try {
-                mService.setMaximumTimeToLock(admin, timeMs, UserHandle.myUserId());
+                mService.setMaximumTimeToLock(admin, timeMs);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1592,7 +1603,7 @@
                             != android.net.Proxy.PROXY_VALID)
                         throw new IllegalArgumentException();
                 }
-                return mService.setGlobalProxy(admin, hostSpec, exclSpec, UserHandle.myUserId());
+                return mService.setGlobalProxy(admin, hostSpec, exclSpec);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1758,7 +1769,7 @@
     public int setStorageEncryption(ComponentName admin, boolean encrypt) {
         if (mService != null) {
             try {
-                return mService.setStorageEncryption(admin, encrypt, UserHandle.myUserId());
+                return mService.setStorageEncryption(admin, encrypt);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1977,7 +1988,7 @@
     public void setCameraDisabled(ComponentName admin, boolean disabled) {
         if (mService != null) {
             try {
-                mService.setCameraDisabled(admin, disabled, UserHandle.myUserId());
+                mService.setCameraDisabled(admin, disabled);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2021,7 +2032,7 @@
     public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
         if (mService != null) {
             try {
-                mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+                mService.setScreenCaptureDisabled(admin, disabled);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2065,7 +2076,7 @@
     public void setAutoTimeRequired(ComponentName admin, boolean required) {
         if (mService != null) {
             try {
-                mService.setAutoTimeRequired(admin, UserHandle.myUserId(), required);
+                mService.setAutoTimeRequired(admin, required);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2106,7 +2117,7 @@
     public void setKeyguardDisabledFeatures(ComponentName admin, int which) {
         if (mService != null) {
             try {
-                mService.setKeyguardDisabledFeatures(admin, which, UserHandle.myUserId());
+                mService.setKeyguardDisabledFeatures(admin, which);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2688,8 +2699,7 @@
             PersistableBundle configuration) {
         if (mService != null) {
             try {
-                mService.setTrustAgentConfiguration(admin, target, configuration,
-                        UserHandle.myUserId());
+                mService.setTrustAgentConfiguration(admin, target, configuration);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2918,9 +2928,9 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageNames List of input method package names.
-     * @return true if setting the restriction succeeded. It will fail if there is
-     *     one or more input method enabled, that are not in the list or user if the foreground
-     *     user.
+     * @return true if setting the restriction succeeded. It will fail if there are
+     *     one or more non-system input methods currently enabled that are not in
+     *     the packageNames list.
      */
     public boolean setPermittedInputMethods(ComponentName admin, List<String> packageNames) {
         if (mService != null) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0ca60c0..67bca4e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -32,34 +32,34 @@
  * {@hide}
  */
 interface IDevicePolicyManager {
-    void setPasswordQuality(in ComponentName who, int quality, int userHandle);
+    void setPasswordQuality(in ComponentName who, int quality);
     int getPasswordQuality(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumLength(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumLength(in ComponentName who, int length);
     int getPasswordMinimumLength(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumUpperCase(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumUpperCase(in ComponentName who, int length);
     int getPasswordMinimumUpperCase(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumLowerCase(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumLowerCase(in ComponentName who, int length);
     int getPasswordMinimumLowerCase(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumLetters(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumLetters(in ComponentName who, int length);
     int getPasswordMinimumLetters(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumNumeric(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumNumeric(in ComponentName who, int length);
     int getPasswordMinimumNumeric(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumSymbols(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumSymbols(in ComponentName who, int length);
     int getPasswordMinimumSymbols(in ComponentName who, int userHandle);
 
-    void setPasswordMinimumNonLetter(in ComponentName who, int length, int userHandle);
+    void setPasswordMinimumNonLetter(in ComponentName who, int length);
     int getPasswordMinimumNonLetter(in ComponentName who, int userHandle);
 
-    void setPasswordHistoryLength(in ComponentName who, int length, int userHandle);
+    void setPasswordHistoryLength(in ComponentName who, int length);
     int getPasswordHistoryLength(in ComponentName who, int userHandle);
 
-    void setPasswordExpirationTimeout(in ComponentName who, long expiration, int userHandle);
+    void setPasswordExpirationTimeout(in ComponentName who, long expiration);
     long getPasswordExpirationTimeout(in ComponentName who, int userHandle);
 
     long getPasswordExpiration(in ComponentName who, int userHandle);
@@ -68,33 +68,33 @@
     int getCurrentFailedPasswordAttempts(int userHandle);
     int getProfileWithMinimumFailedPasswordsForWipe(int userHandle);
 
-    void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num, int userHandle);
+    void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
     int getMaximumFailedPasswordsForWipe(in ComponentName admin, int userHandle);
 
-    boolean resetPassword(String password, int flags, int userHandle);
+    boolean resetPassword(String password, int flags);
 
-    void setMaximumTimeToLock(in ComponentName who, long timeMs, int userHandle);
+    void setMaximumTimeToLock(in ComponentName who, long timeMs);
     long getMaximumTimeToLock(in ComponentName who, int userHandle);
 
     void lockNow();
 
     void wipeData(int flags, int userHandle);
 
-    ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList, int userHandle);
+    ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
     ComponentName getGlobalProxyAdmin(int userHandle);
     void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
 
-    int setStorageEncryption(in ComponentName who, boolean encrypt, int userHandle);
+    int setStorageEncryption(in ComponentName who, boolean encrypt);
     boolean getStorageEncryption(in ComponentName who, int userHandle);
     int getStorageEncryptionStatus(int userHandle);
 
-    void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
+    void setCameraDisabled(in ComponentName who, boolean disabled);
     boolean getCameraDisabled(in ComponentName who, int userHandle);
 
-    void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+    void setScreenCaptureDisabled(in ComponentName who, boolean disabled);
     boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
 
-    void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
+    void setKeyguardDisabledFeatures(in ComponentName who, int which);
     int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
 
     void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing, int userHandle);
@@ -186,7 +186,7 @@
     boolean getCrossProfileCallerIdDisabledForUser(int userId);
 
     void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
-            in PersistableBundle args, int userId);
+            in PersistableBundle args);
     List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
             in ComponentName agent, int userId);
 
@@ -194,7 +194,7 @@
     boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
     List<String> getCrossProfileWidgetProviders(in ComponentName admin);
 
-    void setAutoTimeRequired(in ComponentName who, int userHandle, boolean required);
+    void setAutoTimeRequired(in ComponentName who, boolean required);
     boolean getAutoTimeRequired();
 
     boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 0450150..767f59e 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.media.AudioManager;
 import android.os.IBinder;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -415,9 +416,16 @@
     }
 
     /**
-     * Tells remote device to adjust volume. Only if absolute volume is supported.
+     * Tells remote device to adjust volume. Only if absolute volume is
+     * supported. Uses the following values:
+     * <ul>
+     * <li>{@link AudioManager#ADJUST_LOWER}</li>
+     * <li>{@link AudioManager#ADJUST_RAISE}</li>
+     * <li>{@link AudioManager#ADJUST_MUTE}</li>
+     * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+     * </ul>
      *
-     * @param direction 1 to increase volume, or -1 to decrease volume
+     * @param direction One of the supported adjust values.
      * @hide
      */
     public void adjustAvrcpAbsoluteVolume(int direction) {
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 46c9234..cd32dae 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import android.annotation.Nullable;
+
 import java.util.Map;
 import java.util.Set;
 
@@ -78,7 +80,7 @@
          * @return Returns a reference to the same Editor object, so you can
          * chain put calls together.
          */
-        Editor putString(String key, String value);
+        Editor putString(String key, @Nullable String value);
         
         /**
          * Set a set of String values in the preferences editor, to be written
@@ -91,7 +93,7 @@
          * @return Returns a reference to the same Editor object, so you can
          * chain put calls together.
          */
-        Editor putStringSet(String key, Set<String> values);
+        Editor putStringSet(String key, @Nullable Set<String> values);
         
         /**
          * Set an int value in the preferences editor, to be written back once
@@ -254,7 +256,8 @@
      * 
      * @throws ClassCastException
      */
-    String getString(String key, String defValue);
+    @Nullable
+    String getString(String key, @Nullable String defValue);
     
     /**
      * Retrieve a set of String values from the preferences.
@@ -272,7 +275,8 @@
      * 
      * @throws ClassCastException
      */
-    Set<String> getStringSet(String key, Set<String> defValues);
+    @Nullable
+    Set<String> getStringSet(String key, @Nullable Set<String> defValues);
     
     /**
      * Retrieve an int value from the preferences.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 641f843e..4723c0d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -641,6 +641,13 @@
      */
     public String parentActivityName;
 
+    /**
+     * Value indicating if the activity is resizeable to any dimension.
+     * See {@link android.R.attr#resizeableActivity}.
+     * @hide
+     */
+    public boolean resizeable;
+
     public ActivityInfo() {
     }
 
@@ -702,6 +709,7 @@
         if (uiOptions != 0) {
             pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
         }
+        pw.println(prefix + "resizeable=" + resizeable);
         super.dumpBack(pw, prefix);
     }
     
@@ -730,6 +738,7 @@
         dest.writeString(parentActivityName);
         dest.writeInt(persistableMode);
         dest.writeInt(maxRecents);
+        dest.writeInt(resizeable ? 1 : 0);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -757,5 +766,6 @@
         parentActivityName = source.readString();
         persistableMode = source.readInt();
         maxRecents = source.readInt();
+        resizeable = (source.readInt() == 1);
     }
 }
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index ee23fcd..87b97aa 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -39,6 +39,7 @@
 
     private ActivityInfo mActivityInfo;
     private ComponentName mComponentName;
+    private ResolveInfo mResolveInfo;
     private UserHandle mUser;
     private long mFirstInstallTime;
 
@@ -52,6 +53,7 @@
     LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user,
             long firstInstallTime) {
         this(context);
+        mResolveInfo = info;
         mActivityInfo = info.activityInfo;
         mComponentName = LauncherApps.getComponentName(info);
         mUser = user;
@@ -92,7 +94,7 @@
      * @return The label for the activity.
      */
     public CharSequence getLabel() {
-        return mActivityInfo.loadLabel(mPm);
+        return mResolveInfo.loadLabel(mPm);
     }
 
     /**
@@ -104,8 +106,22 @@
      * @return The drawable associated with the activity
      */
     public Drawable getIcon(int density) {
-        // TODO: Use density
-        return mActivityInfo.loadIcon(mPm);
+        int iconRes = mResolveInfo.getIconResource();
+        Resources resources = null;
+        Drawable icon = null;
+        // Get the preferred density icon from the app's resources
+        if (density != 0 && iconRes != 0) {
+            try {
+                resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
+                icon = resources.getDrawableForDensity(iconRes, density);
+            } catch (NameNotFoundException | Resources.NotFoundException exc) {
+            }
+        }
+        // Get the default density icon
+        if (icon == null) {
+            icon = mResolveInfo.loadIcon(mPm);
+        }
+        return icon;
     }
 
     /**
@@ -151,23 +167,7 @@
      * @return A badged icon for the activity.
      */
     public Drawable getBadgedIcon(int density) {
-        int iconRes = mActivityInfo.getIconResource();
-        Resources resources = null;
-        Drawable originalIcon = null;
-        try {
-            resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
-            try {
-                if (density != 0) {
-                    originalIcon = resources.getDrawableForDensity(iconRes, density);
-                }
-            } catch (Resources.NotFoundException e) {
-            }
-        } catch (NameNotFoundException nnfe) {
-        }
-
-        if (originalIcon == null) {
-            originalIcon = mActivityInfo.loadIcon(mPm);
-        }
+        Drawable originalIcon = getIcon(density);
 
         if (originalIcon instanceof BitmapDrawable) {
             return mPm.getUserBadgedIcon(originalIcon, mUser);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d7d9e8b..04777ba 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -49,6 +49,7 @@
 import android.util.Slog;
 import android.util.TypedValue;
 
+import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 
@@ -2759,9 +2760,17 @@
             }
         }
 
+        addSharedLibrariesForBackwardCompatibility(owner);
+
         return true;
     }
 
+    private static void addSharedLibrariesForBackwardCompatibility(Package owner) {
+        if (owner.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+            owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, "org.apache.http.legacy");
+        }
+    }
+
     /**
      * Parse the {@code application} XML tree at the current parse location in a
      * <em>split APK</em> manifest.
@@ -2946,20 +2955,19 @@
             XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
             boolean receiver, boolean hardwareAccelerated)
             throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
-                com.android.internal.R.styleable.AndroidManifestActivity);
+        TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity);
 
         if (mParseActivityArgs == null) {
             mParseActivityArgs = new ParseComponentArgs(owner, outError,
-                    com.android.internal.R.styleable.AndroidManifestActivity_name,
-                    com.android.internal.R.styleable.AndroidManifestActivity_label,
-                    com.android.internal.R.styleable.AndroidManifestActivity_icon,
-                    com.android.internal.R.styleable.AndroidManifestActivity_logo,
-                    com.android.internal.R.styleable.AndroidManifestActivity_banner,
+                    R.styleable.AndroidManifestActivity_name,
+                    R.styleable.AndroidManifestActivity_label,
+                    R.styleable.AndroidManifestActivity_icon,
+                    R.styleable.AndroidManifestActivity_logo,
+                    R.styleable.AndroidManifestActivity_banner,
                     mSeparateProcesses,
-                    com.android.internal.R.styleable.AndroidManifestActivity_process,
-                    com.android.internal.R.styleable.AndroidManifestActivity_description,
-                    com.android.internal.R.styleable.AndroidManifestActivity_enabled);
+                    R.styleable.AndroidManifestActivity_process,
+                    R.styleable.AndroidManifestActivity_description,
+                    R.styleable.AndroidManifestActivity_enabled);
         }
         
         mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
@@ -2972,22 +2980,18 @@
             return null;
         }
 
-        boolean setExported = sa.hasValue(
-                com.android.internal.R.styleable.AndroidManifestActivity_exported);
+        boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
         if (setExported) {
-            a.info.exported = sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_exported, false);
+            a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false);
         }
 
-        a.info.theme = sa.getResourceId(
-                com.android.internal.R.styleable.AndroidManifestActivity_theme, 0);
+        a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
 
-        a.info.uiOptions = sa.getInt(
-                com.android.internal.R.styleable.AndroidManifestActivity_uiOptions,
+        a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
                 a.info.applicationInfo.uiOptions);
 
         String parentName = sa.getNonConfigurationString(
-                com.android.internal.R.styleable.AndroidManifestActivity_parentActivityName,
+                R.styleable.AndroidManifestActivity_parentActivityName,
                 Configuration.NATIVE_CONFIG_VERSION);
         if (parentName != null) {
             String parentClassName = buildClassName(a.info.packageName, parentName, outError);
@@ -3001,8 +3005,7 @@
         }
 
         String str;
-        str = sa.getNonConfigurationString(
-                com.android.internal.R.styleable.AndroidManifestActivity_permission, 0);
+        str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
         if (str == null) {
             a.info.permission = owner.applicationInfo.permission;
         } else {
@@ -3010,140 +3013,111 @@
         }
 
         str = sa.getNonConfigurationString(
-                com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity,
+                R.styleable.AndroidManifestActivity_taskAffinity,
                 Configuration.NATIVE_CONFIG_VERSION);
         a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
                 owner.applicationInfo.taskAffinity, str, outError);
 
         a.info.flags = 0;
         if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_multiprocess,
-                false)) {
+                R.styleable.AndroidManifestActivity_multiprocess, false)) {
             a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_finishOnTaskLaunch,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
             a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_clearTaskOnLaunch,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
             a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_noHistory,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
             a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_alwaysRetainTaskState,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
             a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_stateNotNeeded,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
             a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_excludeFromRecents,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
             a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_allowTaskReparenting,
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
                 (owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
             a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, false)) {
             a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_showOnLockScreen,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)) {
             a.info.flags |= ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_immersive,
-                false)) {
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
             a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
         }
 
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_primaryUserOnly, false)) {
+            a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
+        }
+
         if (!receiver) {
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated,
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
                     hardwareAccelerated)) {
                 a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
             }
 
             a.info.launchMode = sa.getInt(
-                    com.android.internal.R.styleable.AndroidManifestActivity_launchMode,
-                    ActivityInfo.LAUNCH_MULTIPLE);
+                    R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
             a.info.documentLaunchMode = sa.getInt(
-                    com.android.internal.R.styleable.AndroidManifestActivity_documentLaunchMode,
+                    R.styleable.AndroidManifestActivity_documentLaunchMode,
                     ActivityInfo.DOCUMENT_LAUNCH_NONE);
             a.info.maxRecents = sa.getInt(
-                    com.android.internal.R.styleable.AndroidManifestActivity_maxRecents,
+                    R.styleable.AndroidManifestActivity_maxRecents,
                     ActivityManager.getDefaultAppRecentsLimitStatic());
             a.info.screenOrientation = sa.getInt(
-                    com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation,
+                    R.styleable.AndroidManifestActivity_screenOrientation,
                     ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-            a.info.configChanges = sa.getInt(
-                    com.android.internal.R.styleable.AndroidManifestActivity_configChanges,
-                    0);
+            a.info.configChanges = sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0);
             a.info.softInputMode = sa.getInt(
-                    com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
-                    0);
+                    R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
 
             a.info.persistableMode = sa.getInteger(
-                    com.android.internal.R.styleable.AndroidManifestActivity_persistableMode,
+                    R.styleable.AndroidManifestActivity_persistableMode,
                     ActivityInfo.PERSIST_ROOT_ONLY);
 
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
-                    false)) {
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
                 a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
             }
 
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_autoRemoveFromRecents,
-                    false)) {
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, false)) {
                 a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
             }
 
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
-                    false)) {
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, false)) {
                 a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
             }
 
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_resumeWhilePausing,
-                    false)) {
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
                 a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
             }
+
+            a.info.resizeable = sa.getBoolean(
+                    R.styleable.AndroidManifestActivity_resizeableActivity,
+                    owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.MNC);
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
-        }
 
-        if (receiver) {
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_singleUser,
-                    false)) {
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
                 a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
                 if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
                     Slog.w(TAG, "Activity exported request ignored due to singleUser: "
@@ -3153,11 +3127,6 @@
                     setExported = true;
                 }
             }
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly,
-                    false)) {
-                a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
-            }
         }
 
         sa.recycle();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index c931c01..6fb7299 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -141,9 +141,6 @@
 
     private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
 
-    @SuppressWarnings("unused")
-    private WeakReference<IBinder> mToken;
-
     static {
         sPreloadedDrawables = new LongSparseArray[2];
         sPreloadedDrawables[0] = new LongSparseArray<ConstantState>();
@@ -224,46 +221,44 @@
     /**
      * Create a new Resources object on top of an existing set of assets in an
      * AssetManager.
-     * 
-     * @param assets Previously created AssetManager. 
-     * @param metrics Current display metrics to consider when 
+     *
+     * @param assets Previously created AssetManager.
+     * @param metrics Current display metrics to consider when
      *                selecting/computing resource values.
-     * @param config Desired device configuration to consider when 
+     * @param config Desired device configuration to consider when
      *               selecting/computing resource values (optional).
      */
     public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
-        this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+        this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
     }
 
     /**
      * Creates a new Resources object with CompatibilityInfo.
-     * 
-     * @param assets Previously created AssetManager. 
-     * @param metrics Current display metrics to consider when 
+     *
+     * @param assets Previously created AssetManager.
+     * @param metrics Current display metrics to consider when
      *                selecting/computing resource values.
-     * @param config Desired device configuration to consider when 
+     * @param config Desired device configuration to consider when
      *               selecting/computing resource values (optional).
      * @param compatInfo this resource's compatibility info. Must not be null.
-     * @param token The Activity token for determining stack affiliation. Usually null.
      * @hide
      */
     public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
-            CompatibilityInfo compatInfo, IBinder token) {
+            CompatibilityInfo compatInfo) {
         mAssets = assets;
         mMetrics.setToDefaults();
         if (compatInfo != null) {
             mCompatibilityInfo = compatInfo;
         }
-        mToken = new WeakReference<IBinder>(token);
         updateConfiguration(config, metrics);
         assets.ensureStringBlocks();
     }
 
     /**
      * Return a global shared Resources object that provides access to only
-     * system resources (no application resources), and is not configured for 
-     * the current screen (can not use dimension units, does not change based 
-     * on orientation, etc). 
+     * system resources (no application resources), and is not configured for
+     * the current screen (can not use dimension units, does not change based
+     * on orientation, etc).
      */
     public static Resources getSystem() {
         synchronized (sSync) {
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 4ae3825..9548d49 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -16,27 +16,23 @@
 
 package android.content.res;
 
-import android.os.IBinder;
+import java.util.Objects;
 
 /** @hide */
 public final class ResourcesKey {
-    final String mResDir;
-    final float mScale;
+    private final String mResDir;
+    private final float mScale;
     private final int mHash;
-    private final IBinder mToken;
 
     public final int mDisplayId;
-    public final Configuration mOverrideConfiguration = new Configuration();
+    public final Configuration mOverrideConfiguration;
 
     public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration,
-            float scale, IBinder token) {
+            float scale) {
         mResDir = resDir;
         mDisplayId = displayId;
-        if (overrideConfiguration != null) {
-            mOverrideConfiguration.setTo(overrideConfiguration);
-        }
+        mOverrideConfiguration = overrideConfiguration;
         mScale = scale;
-        mToken = token;
 
         int hash = 17;
         hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode());
@@ -48,7 +44,8 @@
     }
 
     public boolean hasOverrideConfiguration() {
-        return !Configuration.EMPTY.equals(mOverrideConfiguration);
+        return mOverrideConfiguration != null
+                && !Configuration.EMPTY.equals(mOverrideConfiguration);
     }
 
     @Override
@@ -63,17 +60,9 @@
         }
         ResourcesKey peer = (ResourcesKey) obj;
 
-        if ((mResDir == null) && (peer.mResDir != null)) {
+        if (!Objects.equals(mResDir, peer.mResDir)) {
             return false;
         }
-        if ((mResDir != null) && (peer.mResDir == null)) {
-            return false;
-        }
-        if ((mResDir != null) && (peer.mResDir != null)) {
-            if (!mResDir.equals(peer.mResDir)) {
-                return false;
-            }
-        }
         if (mDisplayId != peer.mDisplayId) {
             return false;
         }
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 91ef6ca..84e9912 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -28,16 +28,14 @@
  */
 public class CameraAccessException extends AndroidException {
     /**
-     * The camera device is in use already
-     * @hide
+     * The camera device is in use already.
      */
     public static final int CAMERA_IN_USE = 4;
 
     /**
-     * The system-wide limit for number of open cameras has been reached,
-     * and more camera devices cannot be opened until previous instances are
-     * closed.
-     * @hide
+     * The system-wide limit for number of open cameras or camera resources has
+     * been reached, and more camera devices cannot be opened or torch mode
+     * cannot be turned on until previous instances are closed.
      */
     public static final int MAX_CAMERAS_IN_USE = 5;
 
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index eace1c4..bb45b91 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -391,10 +391,12 @@
      * {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation}, in counts of {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep},
      * that are supported by this camera device.</p>
      * <p><b>Range of valid values:</b><br></p>
+     * <p>Range [0,0] indicates that exposure compensation is not supported.</p>
+     * <p>For LIMITED and FULL devices, range must follow below requirements if exposure
+     * compensation is supported (<code>range != [0, 0]</code>):</p>
      * <p><code>Min.exposure compensation * {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} &lt;= -2 EV</code></p>
      * <p><code>Max.exposure compensation * {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} &gt;= 2 EV</code></p>
-     * <p>LEGACY devices may support a smaller range than this, including the range [0,0], which
-     * indicates that changing the exposure compensation is not supported.</p>
+     * <p>LEGACY devices may support a smaller range than this.</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
@@ -422,6 +424,17 @@
             new Key<Rational>("android.control.aeCompensationStep", Rational.class);
 
     /**
+     * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</p>
+     * <p>LIMITED or FULL devices will always list <code>true</code></p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_LOCK
+     */
+    @PublicKey
+    public static final Key<Boolean> CONTROL_AE_LOCK_AVAILABLE =
+            new Key<Boolean>("android.control.aeLockAvailable", boolean.class);
+
+    /**
      * <p>List of auto-focus (AF) modes for {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} that are
      * supported by this camera device.</p>
      * <p>Not all the auto-focus modes may be supported by a
@@ -469,6 +482,22 @@
             new Key<int[]>("android.control.availableEffects", int[].class);
 
     /**
+     * <p>List of control modes for {@link CaptureRequest#CONTROL_MODE android.control.mode} that are supported by this camera
+     * device.</p>
+     * <p>This list contains control modes that can be set for the camera device.
+     * LEGACY mode devices will always support AUTO mode. LIMITED and FULL
+     * devices will always support OFF, AUTO modes.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#CONTROL_MODE android.control.mode}</p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> CONTROL_AVAILABLE_MODES =
+            new Key<int[]>("android.control.availableModes", int[].class);
+
+    /**
      * <p>List of scene modes for {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} that are supported by this camera
      * device.</p>
      * <p>This list contains scene modes that can be set for the camera device.
@@ -530,6 +559,17 @@
             new Key<int[]>("android.control.awbAvailableModes", int[].class);
 
     /**
+     * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</p>
+     * <p>LIMITED or FULL devices will always list <code>true</code></p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AWB_LOCK
+     */
+    @PublicKey
+    public static final Key<Boolean> CONTROL_AWB_LOCK_AVAILABLE =
+            new Key<Boolean>("android.control.awbLockAvailable", boolean.class);
+
+    /**
      * <p>List of the maximum number of regions that can be used for metering in
      * auto-exposure (AE), auto-white balance (AWB), and auto-focus (AF);
      * this corresponds to the the maximum number of elements in
@@ -1069,8 +1109,11 @@
      * android.scaler.availableInputOutputFormatsMap. When using an
      * input stream, there must be at least one output stream
      * configured to to receive the reprocessed images.</p>
+     * <p>When an input stream and some output streams are used in a reprocessing request,
+     * only the input buffer will be used to produce these output stream buffers, and a
+     * new sensor image will not be captured.</p>
      * <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input
-     * stream image format will be RAW_OPAQUE, the associated output stream image format
+     * stream image format will be OPAQUE, the associated output stream image format
      * should be JPEG.</p>
      * <p><b>Range of valid values:</b><br></p>
      * <p>0 or 1.</p>
@@ -1080,8 +1123,8 @@
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
-     * @hide
      */
+    @PublicKey
     public static final Key<Integer> REQUEST_MAX_NUM_INPUT_STREAMS =
             new Key<Integer>("android.request.maxNumInputStreams", int.class);
 
@@ -1157,8 +1200,10 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING MANUAL_POST_PROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1167,8 +1212,10 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1345,10 +1392,10 @@
      * <p>The mapping of image formats that are supported by this
      * camera device for input streams, to their corresponding output formats.</p>
      * <p>All camera devices with at least 1
-     * android.request.maxNumInputStreams will have at least one
+     * {@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} will have at least one
      * available input format.</p>
      * <p>The camera device will support the following map of formats,
-     * if its dependent capability is supported:</p>
+     * if its dependent capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}) is supported:</p>
      * <table>
      * <thead>
      * <tr>
@@ -1359,45 +1406,42 @@
      * </thead>
      * <tbody>
      * <tr>
-     * <td align="left">RAW_OPAQUE</td>
+     * <td align="left">OPAQUE</td>
      * <td align="left">JPEG</td>
-     * <td align="left">ZSL</td>
+     * <td align="left">OPAQUE_REPROCESSING</td>
      * </tr>
      * <tr>
-     * <td align="left">RAW_OPAQUE</td>
+     * <td align="left">OPAQUE</td>
      * <td align="left">YUV_420_888</td>
-     * <td align="left">ZSL</td>
+     * <td align="left">OPAQUE_REPROCESSING</td>
      * </tr>
      * <tr>
-     * <td align="left">RAW_OPAQUE</td>
-     * <td align="left">RAW16</td>
-     * <td align="left">RAW</td>
-     * </tr>
-     * <tr>
-     * <td align="left">RAW16</td>
      * <td align="left">YUV_420_888</td>
-     * <td align="left">RAW</td>
-     * </tr>
-     * <tr>
-     * <td align="left">RAW16</td>
      * <td align="left">JPEG</td>
-     * <td align="left">RAW</td>
+     * <td align="left">YUV_REPROCESSING</td>
+     * </tr>
+     * <tr>
+     * <td align="left">YUV_420_888</td>
+     * <td align="left">YUV_420_888</td>
+     * <td align="left">YUV_REPROCESSING</td>
      * </tr>
      * </tbody>
      * </table>
-     * <p>For ZSL-capable camera devices, using the RAW_OPAQUE format
+     * <p>OPAQUE refers to a device-internal format that is not directly application-visible.
+     * An OPAQUE input or output surface can be acquired by
+     * OpaqueImageRingBufferQueue#getInputSurface() or
+     * OpaqueImageRingBufferQueue#getOutputSurface().
+     * For a OPAQUE_REPROCESSING-capable camera device, using the OPAQUE format
      * as either input or output will never hurt maximum frame rate (i.e.
-     * StreamConfigurationMap#getOutputStallDuration(int,Size)
-     * for a <code>format =</code> RAW_OPAQUE is always 0).</p>
+     * StreamConfigurationMap#getOutputStallDuration(klass,Size) is always 0),
+     * where klass is android.media.OpaqueImageRingBufferQueue.class.</p>
      * <p>Attempting to configure an input stream with output streams not
      * listed as available in this map is not valid.</p>
      * <p>TODO: typedef to ReprocessFormatMap</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     * <p><b>Full capability</b> -
-     * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
-     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
-     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
      * @hide
      */
     public static final Key<int[]> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP =
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b6bb33b..8af3c15 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -109,8 +109,11 @@
      * of the state of individual CameraManager instances.</p>
      *
      * @param callback the new callback to send camera availability notices to
-     * @param handler The handler on which the callback should be invoked, or
-     * {@code null} to use the current thread's {@link android.os.Looper looper}.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *             the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
+     *             no looper.
      */
     public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
         if (handler == null) {
@@ -138,6 +141,42 @@
     }
 
     /**
+     * Register a callback to be notified about torch mode status.
+     *
+     * <p>Registering the same callback again will replace the handler with the
+     * new one provided.</p>
+     *
+     * <p>The first time a callback is registered, it is immediately called
+     * with the torch mode status of all currently known camera devices.</p>
+     *
+     * <p>Since this callback will be registered with the camera service, remember to unregister it
+     * once it is no longer needed; otherwise the callback will continue to receive events
+     * indefinitely and it may prevent other resources from being released. Specifically, the
+     * callbacks will be invoked independently of the general activity lifecycle and independently
+     * of the state of individual CameraManager instances.</p>
+     *
+     * @param callback The new callback to send torch mode status to
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *             the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
+     *             no looper.
+     */
+    public void registerTorchCallback(TorchCallback callback, Handler handler) {
+    }
+
+    /**
+     * Remove a previously-added callback; the callback will no longer receive torch mode status
+     * callbacks.
+     *
+     * <p>Removing a callback that isn't registered has no effect.</p>
+     *
+     * @param callback The callback to remove from the notification list
+     */
+    public void unregisterTorchCallback(TorchCallback callback) {
+    }
+
+    /**
      * <p>Query the capabilities of a camera device. These capabilities are
      * immutable for a given camera.</p>
      *
@@ -384,6 +423,47 @@
     }
 
     /**
+     * Set the flash unit's torch mode of the camera of the given ID without opening the camera
+     * device.
+     *
+     * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
+     * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
+     * Note that even if a camera device has a flash unit, turning on the torch mode may fail
+     * if the camera device or other camera resources needed to turn on the torch mode are in use.
+     * </p>
+     *
+     * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
+     * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
+     * However, even if turning on the torch mode is successful, the application does not have the
+     * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
+     * off and becomes unavailable when the camera device that the flash unit belongs to becomes
+     * unavailable ({@link CameraManager.TorchCallback#onTorchModeAvailable} will be
+     * invoked) or when other camera resources to keep the torch on become unavailable (
+     * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
+     * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
+     * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked).
+     *
+     * @param cameraId
+     *             The unique identifier of the camera device that the flash unit belongs to.
+     * @param enabled
+     *             The desired state of the torch mode for the target camera device. Set to
+     *             {@code true} to turn on the torch mode. Set to {@code false} to turn off the
+     *             torch mode.
+     *
+     * @throws CameraAccessException if it failed to access the flash unit.
+     *             {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
+     *             is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
+     *             other camera resources needed to turn on the torch mode are in use.
+     *
+     * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
+     *             or previously available camera device, or the camera device doesn't have a
+     *             flash unit.
+     */
+    public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
+
+    }
+
+    /**
      * A callback for camera devices becoming available or
      * unavailable to open.
      *
@@ -428,6 +508,68 @@
     }
 
     /**
+     * A callback for camera flash torch modes becoming available, unavailable, enabled, or
+     * disabled.
+     *
+     * <p>The torch mode becomes available when the camera device it belongs to is no longer in use
+     * and other camera resources it needs are no longer busy. It becomes unavailable when the
+     * camera device it belongs to becomes unavailable or other camera resouces it needs become
+     * busy due to other higher priority camera activities. The torch mode changes when an
+     * application calls {@link #setTorchMode} successfully.
+     *
+     * <p>Extend this callback and pass an instance of the subclass to
+     * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
+     * </p>
+     *
+     * @see registerTorchCallback
+     */
+    public static abstract class TorchCallback {
+        /**
+         * The torch mode of a camera has become available to use.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the camera whose torch mode has become
+         *                 available.
+         */
+        public void onTorchModeAvailable(String cameraId) {
+            // default empty implementation
+        }
+
+        /**
+         * A previously-available torch mode of a camera has become unavailable.
+         *
+         * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
+         * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
+         * invoked. {@link #setTorchMode} will fail until the flash unit becomes available again.
+         * </p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the camera whose torch mode has become
+         *                 unavailable.
+         */
+        public void onTorchModeUnavailable(String cameraId) {
+            // default empty implementation
+        }
+
+        /**
+         * Torch mode of a camera has been turned on or off through {@link #setTorchMode}.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the camera whose torch mode has been changed.
+         *
+         * @param enabled The state that the torch mode of the camera has been changed to.
+         *                {@code true} when the torch mode has been turned on. {@code false} when
+         *                the torch mode has been turned off.
+         */
+        public void onTorchModeChanged(String cameraId, boolean enabled) {
+            // default empty implementation
+        }
+    }
+
+    /**
      * Return or create the list of currently connected camera devices.
      *
      * <p>In case of errors connecting to the camera service, will return an empty list.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index f6df302..af7d365 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -439,23 +439,40 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
 
     /**
-     * <p>The camera device supports the Zero Shutter Lag use case.</p>
+     * <p>The camera device supports the Zero Shutter Lag reprocessing use case.</p>
      * <ul>
-     * <li>At least one input stream can be used.</li>
-     * <li>RAW_OPAQUE is supported as an output/input format</li>
-     * <li>Using RAW_OPAQUE does not cause a frame rate drop
+     * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
+     * <li>OPAQUE is supported as an output/input format, that is,
+     *   StreamConfigurationMap#getOutputSizes(klass) and
+     *   StreamConfigurationMap#getInputSizes(klass) return non empty Size[] and have common
+     *   sizes, where klass is android.media.OpaqueImageRingBufferQueue.class. See
+     *   android.scaler.availableInputOutputFormatsMap for detailed information about
+     *   OPAQUE format.</li>
+     * <li>android.scaler.availableInputOutputFormatsMap has the required map entries.</li>
+     * <li>Using OPAQUE does not cause a frame rate drop
      *   relative to the sensor's maximum capture rate (at that
-     *   resolution).</li>
-     * <li>RAW_OPAQUE will be reprocessable into both YUV_420_888
+     *   resolution), see android.scaler.availableInputOutputFormatsMap for more details.</li>
+     * <li>OPAQUE will be reprocessable into both YUV_420_888
      *   and JPEG formats.</li>
-     * <li>The maximum available resolution for RAW_OPAQUE streams
+     * <li>The maximum available resolution for OPAQUE streams
      *   (both input/output) will match the maximum available
      *   resolution of JPEG streams.</li>
+     * <li>Only below controls are effective for reprocessing requests and
+     *   will be present in capture results, other controls in reprocess
+     *   requests will be ignored by the camera device.<ul>
+     * <li>android.jpeg.*</li>
+     * <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
+     * <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
      * </ul>
+     * </li>
+     * </ul>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
-     * @hide
      */
-    public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4;
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4;
 
     /**
      * <p>The camera device supports accurately reporting the sensor settings for many of
@@ -515,6 +532,45 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6;
 
+    /**
+     * <p>The camera device supports the YUV420_888 reprocessing use case, similar as
+     * OPAQUE_REPROCESSING, This capability requires the camera device to support the
+     * following:</p>
+     * <ul>
+     * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
+     * <li>YUV420_888 is supported as a common format for both input and output, that is,
+     *   StreamConfigurationMap#getOutputSizes(YUV420_888) and
+     *   StreamConfigurationMap#getInputSizes(YUV420_888) return non empty Size[] and have
+     *   common sizes.</li>
+     * <li>android.scaler.availableInputOutputFormatsMap has the required map entries.</li>
+     * <li>Using YUV420_888 does not cause a frame rate drop
+     *   relative to the sensor's maximum capture rate (at that
+     *   resolution), see android.scaler.availableInputOutputFormatsMap for more details.</li>
+     * <li>YUV420_888 will be reprocessable into both YUV_420_888
+     *   and JPEG formats.</li>
+     * <li>The maximum available resolution for YUV420_888 streams
+     *   (both input/output) will match the maximum available
+     *   resolution of JPEG streams.</li>
+     * <li>Only the below controls are effective for reprocessing requests and will be
+     *   present in capture results. The reprocess requests are from the original capture
+     *   results that are assocaited with the intermidate YUV420_888 output buffers.
+     *   All other controls in the reprocess requests will be ignored by the camera device.<ul>
+     * <li>android.jpeg.*</li>
+     * <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
+     * <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
+     * <li>{@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}</li>
+     * </ul>
+     * </li>
+     * </ul>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -1830,6 +1886,13 @@
      */
     public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2;
 
+    /**
+     * <p>MINIMAL noise reduction is applied without reducing frame rate relative to
+     * sensor output. </p>
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     */
+    public static final int NOISE_REDUCTION_MODE_MINIMAL = 3;
+
     //
     // Enumeration values for CaptureRequest#SENSOR_TEST_PATTERN_MODE
     //
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0849df8..a40d47a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -552,6 +552,8 @@
      * in this matrix result metadata. The transform should keep the magnitude
      * of the output color values within <code>[0, 1.0]</code> (assuming input color
      * values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p>
+     * <p>The valid range of each matrix element varies on different devices, but
+     * values within [-1.5, 3.0] are guaranteed not to be clipped.</p>
      * <p><b>Units</b>: Unitless scale factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -575,6 +577,10 @@
      * TRANSFORM_MATRIX.</p>
      * <p>The gains in the result metadata are the gains actually
      * applied by the camera device to the current frame.</p>
+     * <p>The valid range of gains varies on different devices, but gains
+     * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given
+     * device allows gains below 1.0, this is usually not recommended because
+     * this can create color artifacts.</p>
      * <p><b>Units</b>: Unitless gain factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -867,9 +873,9 @@
      * metering sequence when it processes this request.</p>
      * <p>This entry is normally set to IDLE, or is not
      * included at all in the request settings. When included and
-     * set to START, the camera device will trigger the autoexposure
+     * set to START, the camera device will trigger the auto-exposure (AE)
      * precapture metering sequence.</p>
-     * <p>The precapture sequence should triggered before starting a
+     * <p>The precapture sequence should be triggered before starting a
      * high-quality still capture for final metering decisions to
      * be made, and for firing pre-capture flash pulses to estimate
      * scene brightness and required final capture flash power, when
@@ -877,6 +883,14 @@
      * <p>Normally, this entry should be set to START for only a
      * single request, and the application should wait until the
      * sequence completes before starting a new one.</p>
+     * <p>When a precapture metering sequence is finished, the camera device
+     * may lock the auto-exposure routine internally to be able to accurately expose the
+     * subsequent still capture image (<code>{@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE</code>).
+     * For this case, the AE may not resume normal scan if no subsequent still capture is
+     * submitted. To ensure that the AE routine restarts normal scan, the application should
+     * submit a request with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == true</code>, followed by a request
+     * with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == false</code>, if the application decides not to submit a
+     * still capture request after the precapture sequence completes.</p>
      * <p>The exact effect of auto-exposure (AE) precapture trigger
      * depends on the current AE mode and state; see
      * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition
@@ -895,7 +909,9 @@
      * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
+     * @see CaptureRequest#CONTROL_AE_LOCK
      * @see CaptureResult#CONTROL_AE_STATE
+     * @see CaptureRequest#CONTROL_CAPTURE_INTENT
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
@@ -1154,7 +1170,7 @@
      * <p>This control (except for MANUAL) is only effective if
      * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
      * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
-     * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+     * contains OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
      * contains MANUAL_SENSOR. Other intent values are always supported.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -1239,10 +1255,6 @@
      * update, as if this frame is never captured. This mode can be used in the scenario
      * where the application doesn't want a 3A manual control capture to affect
      * the subsequent auto 3A capture results.</p>
-     * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes.
-     * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they
-     * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities.
-     * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_MODE_OFF OFF}</li>
@@ -1250,9 +1262,12 @@
      *   <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li>
      *   <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AF_MODE
+     * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES
      * @see #CONTROL_MODE_OFF
      * @see #CONTROL_MODE_AUTO
      * @see #CONTROL_MODE_USE_SCENE_MODE
@@ -1372,6 +1387,10 @@
      * camera device will use the highest-quality enhancement algorithms,
      * even if it slows down capture rate. FAST means the camera device will
      * not slow down capture rate when applying edge enhancement.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera
+     * device will apply FAST/HIGH_QUALITY YUV-domain edge enhancement, respectively.
+     * The camera device may adjust its internal noise reduction parameters for best
+     * image quality based on the {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}, if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #EDGE_MODE_OFF OFF}</li>
@@ -1387,6 +1406,7 @@
      *
      * @see CameraCharacteristics#EDGE_AVAILABLE_EDGE_MODES
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #EDGE_MODE_OFF
      * @see #EDGE_MODE_FAST
      * @see #EDGE_MODE_HIGH_QUALITY
@@ -1751,18 +1771,28 @@
     /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
-     * excessive noise added by the capture process, especially in dark conditions.
-     * OFF means no noise reduction will be applied by the camera device.</p>
+     * excessive noise added by the capture process, especially in dark conditions.</p>
+     * <p>OFF means no noise reduction will be applied by the camera device, for both raw and
+     * YUV domain.</p>
+     * <p>MINIMAL means that only sensor raw domain basic noise reduction is enabled ,to remove
+     * demosaicing or other processing artifacts. For YUV_REPROCESSING, MINIMAL is same as OFF.
+     * This mode is optional, may not be support by all devices. The application should check
+     * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes} before using it.</p>
      * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering
      * will be applied. HIGH_QUALITY mode indicates that the camera device
      * will use the highest-quality noise filtering algorithms,
      * even if it slows down capture rate. FAST means the camera device will not
      * slow down capture rate when applying noise filtering.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera device
+     * will apply FAST/HIGH_QUALITY YUV domain noise reduction, respectively. The camera device
+     * may adjust the noise reduction parameters for best image quality based on the
+     * {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor} if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #NOISE_REDUCTION_MODE_OFF OFF}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_FAST FAST}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     *   <li>{@link #NOISE_REDUCTION_MODE_MINIMAL MINIMAL}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
      * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}</p>
@@ -1773,9 +1803,11 @@
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #NOISE_REDUCTION_MODE_OFF
      * @see #NOISE_REDUCTION_MODE_FAST
      * @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
+     * @see #NOISE_REDUCTION_MODE_MINIMAL
      */
     @PublicKey
     public static final Key<Integer> NOISE_REDUCTION_MODE =
@@ -2416,6 +2448,52 @@
     public static final Key<Boolean> BLACK_LEVEL_LOCK =
             new Key<Boolean>("android.blackLevel.lock", boolean.class);
 
+    /**
+     * <p>The amount of exposure time increase factor applied to the original output
+     * frame by the application processing before sending for reprocessing.</p>
+     * <p>This is optional, and will be supported if the camera device supports YUV_REPROCESSING
+     * capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains YUV_REPROCESSING).</p>
+     * <p>For some YUV reprocessing use cases, the application may choose to filter the original
+     * output frames to effectively reduce the noise to the same level as a frame that was
+     * captured with longer exposure time. To be more specific, assuming the original captured
+     * images were captured with a sensitivity of S and an exposure time of T, the model in
+     * the camera device is that the amount of noise in the image would be approximately what
+     * would be expected if the original capture parameters had been a sensitivity of
+     * S/effectiveExposureFactor and an exposure time of T*effectiveExposureFactor, rather
+     * than S and T respectively. If the captured images were processed by the application
+     * before being sent for reprocessing, then the application may have used image processing
+     * algorithms and/or multi-frame image fusion to reduce the noise in the
+     * application-processed images (input images). By using the effectiveExposureFactor
+     * control, the application can communicate to the camera device the actual noise level
+     * improvement in the application-processed image. With this information, the camera
+     * device can select appropriate noise reduction and edge enhancement parameters to avoid
+     * excessive noise reduction ({@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}) and insufficient edge
+     * enhancement ({@link CaptureRequest#EDGE_MODE android.edge.mode}) being applied to the reprocessed frames.</p>
+     * <p>For example, for multi-frame image fusion use case, the application may fuse
+     * multiple output frames together to a final frame for reprocessing. When N image are
+     * fused into 1 image for reprocessing, the exposure time increase factor could be up to
+     * square root of N (based on a simple photon shot noise model). The camera device will
+     * adjust the reprocessing noise reduction and edge enhancement parameters accordingly to
+     * produce the best quality images.</p>
+     * <p>This is relative factor, 1.0 indicates the application hasn't processed the input
+     * buffer in a way that affects its effective exposure time.</p>
+     * <p>This control is only effective for YUV reprocessing capture request. For noise
+     * reduction reprocessing, it is only effective when <code>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} != OFF</code>.
+     * Similarly, for edge enhancement reprocessing, it is only effective when
+     * <code>{@link CaptureRequest#EDGE_MODE android.edge.mode} != OFF</code>.</p>
+     * <p><b>Units</b>: Relative exposure time increase factor.</p>
+     * <p><b>Range of valid values:</b><br>
+     * &gt;= 1.0</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    @PublicKey
+    public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
+            new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1396940..810d0f6 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -403,6 +403,8 @@
      * in this matrix result metadata. The transform should keep the magnitude
      * of the output color values within <code>[0, 1.0]</code> (assuming input color
      * values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p>
+     * <p>The valid range of each matrix element varies on different devices, but
+     * values within [-1.5, 3.0] are guaranteed not to be clipped.</p>
      * <p><b>Units</b>: Unitless scale factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -426,6 +428,10 @@
      * TRANSFORM_MATRIX.</p>
      * <p>The gains in the result metadata are the gains actually
      * applied by the camera device to the current frame.</p>
+     * <p>The valid range of gains varies on different devices, but gains
+     * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given
+     * device allows gains below 1.0, this is usually not recommended because
+     * this can create color artifacts.</p>
      * <p><b>Units</b>: Unitless gain factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -718,9 +724,9 @@
      * metering sequence when it processes this request.</p>
      * <p>This entry is normally set to IDLE, or is not
      * included at all in the request settings. When included and
-     * set to START, the camera device will trigger the autoexposure
+     * set to START, the camera device will trigger the auto-exposure (AE)
      * precapture metering sequence.</p>
-     * <p>The precapture sequence should triggered before starting a
+     * <p>The precapture sequence should be triggered before starting a
      * high-quality still capture for final metering decisions to
      * be made, and for firing pre-capture flash pulses to estimate
      * scene brightness and required final capture flash power, when
@@ -728,6 +734,14 @@
      * <p>Normally, this entry should be set to START for only a
      * single request, and the application should wait until the
      * sequence completes before starting a new one.</p>
+     * <p>When a precapture metering sequence is finished, the camera device
+     * may lock the auto-exposure routine internally to be able to accurately expose the
+     * subsequent still capture image (<code>{@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE</code>).
+     * For this case, the AE may not resume normal scan if no subsequent still capture is
+     * submitted. To ensure that the AE routine restarts normal scan, the application should
+     * submit a request with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == true</code>, followed by a request
+     * with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == false</code>, if the application decides not to submit a
+     * still capture request after the precapture sequence completes.</p>
      * <p>The exact effect of auto-exposure (AE) precapture trigger
      * depends on the current AE mode and state; see
      * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition
@@ -746,7 +760,9 @@
      * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
+     * @see CaptureRequest#CONTROL_AE_LOCK
      * @see CaptureResult#CONTROL_AE_STATE
+     * @see CaptureRequest#CONTROL_CAPTURE_INTENT
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
@@ -1627,7 +1643,7 @@
      * <p>This control (except for MANUAL) is only effective if
      * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
      * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
-     * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+     * contains OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
      * contains MANUAL_SENSOR. Other intent values are always supported.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -1855,10 +1871,6 @@
      * update, as if this frame is never captured. This mode can be used in the scenario
      * where the application doesn't want a 3A manual control capture to affect
      * the subsequent auto 3A capture results.</p>
-     * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes.
-     * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they
-     * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities.
-     * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_MODE_OFF OFF}</li>
@@ -1866,9 +1878,12 @@
      *   <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li>
      *   <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AF_MODE
+     * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES
      * @see #CONTROL_MODE_OFF
      * @see #CONTROL_MODE_AUTO
      * @see #CONTROL_MODE_USE_SCENE_MODE
@@ -1988,6 +2003,10 @@
      * camera device will use the highest-quality enhancement algorithms,
      * even if it slows down capture rate. FAST means the camera device will
      * not slow down capture rate when applying edge enhancement.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera
+     * device will apply FAST/HIGH_QUALITY YUV-domain edge enhancement, respectively.
+     * The camera device may adjust its internal noise reduction parameters for best
+     * image quality based on the {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}, if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #EDGE_MODE_OFF OFF}</li>
@@ -2003,6 +2022,7 @@
      *
      * @see CameraCharacteristics#EDGE_AVAILABLE_EDGE_MODES
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #EDGE_MODE_OFF
      * @see #EDGE_MODE_FAST
      * @see #EDGE_MODE_HIGH_QUALITY
@@ -2465,18 +2485,28 @@
     /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
-     * excessive noise added by the capture process, especially in dark conditions.
-     * OFF means no noise reduction will be applied by the camera device.</p>
+     * excessive noise added by the capture process, especially in dark conditions.</p>
+     * <p>OFF means no noise reduction will be applied by the camera device, for both raw and
+     * YUV domain.</p>
+     * <p>MINIMAL means that only sensor raw domain basic noise reduction is enabled ,to remove
+     * demosaicing or other processing artifacts. For YUV_REPROCESSING, MINIMAL is same as OFF.
+     * This mode is optional, may not be support by all devices. The application should check
+     * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes} before using it.</p>
      * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering
      * will be applied. HIGH_QUALITY mode indicates that the camera device
      * will use the highest-quality noise filtering algorithms,
      * even if it slows down capture rate. FAST means the camera device will not
      * slow down capture rate when applying noise filtering.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera device
+     * will apply FAST/HIGH_QUALITY YUV domain noise reduction, respectively. The camera device
+     * may adjust the noise reduction parameters for best image quality based on the
+     * {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor} if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #NOISE_REDUCTION_MODE_OFF OFF}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_FAST FAST}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     *   <li>{@link #NOISE_REDUCTION_MODE_MINIMAL MINIMAL}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
      * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}</p>
@@ -2487,9 +2517,11 @@
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #NOISE_REDUCTION_MODE_OFF
      * @see #NOISE_REDUCTION_MODE_FAST
      * @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
+     * @see #NOISE_REDUCTION_MODE_MINIMAL
      */
     @PublicKey
     public static final Key<Integer> NOISE_REDUCTION_MODE =
@@ -3647,6 +3679,52 @@
     public static final Key<Long> SYNC_FRAME_NUMBER =
             new Key<Long>("android.sync.frameNumber", long.class);
 
+    /**
+     * <p>The amount of exposure time increase factor applied to the original output
+     * frame by the application processing before sending for reprocessing.</p>
+     * <p>This is optional, and will be supported if the camera device supports YUV_REPROCESSING
+     * capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains YUV_REPROCESSING).</p>
+     * <p>For some YUV reprocessing use cases, the application may choose to filter the original
+     * output frames to effectively reduce the noise to the same level as a frame that was
+     * captured with longer exposure time. To be more specific, assuming the original captured
+     * images were captured with a sensitivity of S and an exposure time of T, the model in
+     * the camera device is that the amount of noise in the image would be approximately what
+     * would be expected if the original capture parameters had been a sensitivity of
+     * S/effectiveExposureFactor and an exposure time of T*effectiveExposureFactor, rather
+     * than S and T respectively. If the captured images were processed by the application
+     * before being sent for reprocessing, then the application may have used image processing
+     * algorithms and/or multi-frame image fusion to reduce the noise in the
+     * application-processed images (input images). By using the effectiveExposureFactor
+     * control, the application can communicate to the camera device the actual noise level
+     * improvement in the application-processed image. With this information, the camera
+     * device can select appropriate noise reduction and edge enhancement parameters to avoid
+     * excessive noise reduction ({@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}) and insufficient edge
+     * enhancement ({@link CaptureRequest#EDGE_MODE android.edge.mode}) being applied to the reprocessed frames.</p>
+     * <p>For example, for multi-frame image fusion use case, the application may fuse
+     * multiple output frames together to a final frame for reprocessing. When N image are
+     * fused into 1 image for reprocessing, the exposure time increase factor could be up to
+     * square root of N (based on a simple photon shot noise model). The camera device will
+     * adjust the reprocessing noise reduction and edge enhancement parameters accordingly to
+     * produce the best quality images.</p>
+     * <p>This is relative factor, 1.0 indicates the application hasn't processed the input
+     * buffer in a way that affects its effective exposure time.</p>
+     * <p>This control is only effective for YUV reprocessing capture request. For noise
+     * reduction reprocessing, it is only effective when <code>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} != OFF</code>.
+     * Similarly, for edge enhancement reprocessing, it is only effective when
+     * <code>{@link CaptureRequest#EDGE_MODE android.edge.mode} != OFF</code>.</p>
+     * <p><b>Units</b>: Relative exposure time increase factor.</p>
+     * <p><b>Range of valid values:</b><br>
+     * &gt;= 1.0</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    @PublicKey
+    public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
+            new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 6fc99ac..33d539c2 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -530,9 +530,9 @@
         int uPixStride = uPlane.getPixelStride();
 
         byte[] yuvPixel = { 0, 0, 0 };
-        byte[] yFullRow = new byte[yPixStride * width];
-        byte[] uFullRow = new byte[uPixStride * width / 2];
-        byte[] vFullRow = new byte[vPixStride * width / 2];
+        byte[] yFullRow = new byte[yPixStride * (width - 1) + 1];
+        byte[] uFullRow = new byte[uPixStride * (width / 2 - 1) + 1];
+        byte[] vFullRow = new byte[vPixStride * (width / 2 - 1) + 1];
         byte[] finalRow = new byte[BYTES_PER_RGB_PIX * width];
         for (int i = 0; i < height; i++) {
             int halfH = i / 2;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 03540e1..802b938 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -328,15 +328,15 @@
         appendStreamConfig(availableStreamConfigs,
                 ImageFormat.YUV_420_888, previewSizes);
         for (int format : p.getSupportedPreviewFormats()) {
-            if (ImageFormat.isPublicFormat(format)) {
+            if (ImageFormat.isPublicFormat(format) && format != ImageFormat.NV21) {
                 appendStreamConfig(availableStreamConfigs, format, previewSizes);
-            } else {
+            } else if (VERBOSE) {
                 /*
                  *  Do not add any formats unknown to us
                  * (since it would fail runtime checks in StreamConfigurationMap)
                  */
-                Log.w(TAG,
-                        String.format("mapStreamConfigs - Skipping non-public format %x", format));
+                Log.v(TAG,
+                        String.format("mapStreamConfigs - Skipping format %x", format));
             }
         }
 
@@ -389,8 +389,8 @@
             int j = 0;
             for (String mode : antiBandingModes) {
                 int convertedMode = convertAntiBandingMode(mode);
-                if (convertedMode == -1) {
-                    Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
+                if (VERBOSE && convertedMode == -1) {
+                    Log.v(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
                             " not supported, skipping...");
                 } else {
                     modes[j++] = convertedMode;
@@ -474,6 +474,15 @@
 
             m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step));
         }
+
+        /*
+         * control.aeLockAvailable
+         */
+        {
+            boolean aeLockAvailable = p.isAutoExposureLockSupported();
+
+            m.set(CONTROL_AE_LOCK_AVAILABLE, aeLockAvailable);
+        }
     }
 
 
@@ -571,6 +580,16 @@
                 Log.v(TAG, "mapControlAwb - control.awbAvailableModes set to " +
                         ListUtils.listToString(awbAvail));
             }
+
+
+            /*
+             * control.awbLockAvailable
+             */
+            {
+                boolean awbLockAvailable = p.isAutoWhiteBalanceLockSupported();
+
+                m.set(CONTROL_AWB_LOCK_AVAILABLE, awbLockAvailable);
+            }
         }
     }
 
@@ -618,17 +637,44 @@
         /*
          * android.control.availableSceneModes
          */
+        int maxNumDetectedFaces = p.getMaxNumDetectedFaces();
         List<String> sceneModes = p.getSupportedSceneModes();
         List<Integer> supportedSceneModes =
                 ArrayUtils.convertStringListToIntList(sceneModes, sLegacySceneModes, sSceneModes);
-        if (supportedSceneModes == null) { // camera1 doesn't support scene mode settings
-            supportedSceneModes = new ArrayList<Integer>();
-            supportedSceneModes.add(CONTROL_SCENE_MODE_DISABLED); // disabled is always available
+
+        // Special case where the only scene mode listed is AUTO => no scene mode
+        if (sceneModes != null && sceneModes.size() == 1 &&
+                sceneModes.get(0) == Parameters.SCENE_MODE_AUTO) {
+            supportedSceneModes = null;
         }
-        if (p.getMaxNumDetectedFaces() > 0) { // always supports FACE_PRIORITY when face detecting
-            supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
+
+        boolean sceneModeSupported = true;
+        if (supportedSceneModes == null && maxNumDetectedFaces == 0) {
+            sceneModeSupported = false;
         }
-        m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
+
+        if (sceneModeSupported) {
+            if (supportedSceneModes == null) {
+                supportedSceneModes = new ArrayList<Integer>();
+            }
+            if (maxNumDetectedFaces > 0) { // always supports FACE_PRIORITY when face detecting
+                supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
+            }
+            // Remove all DISABLED occurrences
+            if (supportedSceneModes.contains(CONTROL_SCENE_MODE_DISABLED)) {
+                while(supportedSceneModes.remove(new Integer(CONTROL_SCENE_MODE_DISABLED))) {}
+            }
+            m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
+        } else {
+            m.set(CONTROL_AVAILABLE_SCENE_MODES, new int[] {CONTROL_SCENE_MODE_DISABLED});
+        }
+
+        /*
+         * android.control.availableModes
+         */
+        m.set(CONTROL_AVAILABLE_MODES, sceneModeSupported ?
+                new int[] { CONTROL_MODE_AUTO, CONTROL_MODE_USE_SCENE_MODE } :
+                new int[] { CONTROL_MODE_AUTO });
     }
 
     private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 6535a4e..f1f2f0c 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -44,6 +44,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -68,7 +69,7 @@
     // For slightly more spammy messages that will get repeated every frame
     private static final boolean VERBOSE =
             Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE);
-    private final Camera mCamera;
+    private Camera mCamera;
     private final CameraCharacteristics mCharacteristics;
 
     private final CameraDeviceState mDeviceState;
@@ -83,8 +84,8 @@
     private static final int MAX_IN_FLIGHT_REQUESTS = 2;
 
     private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
-    private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
-    private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout)
+    private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2)
+    private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT; // ms (same as JPEG timeout)
 
     private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
     private boolean mPreviewRunning = false;
@@ -108,6 +109,8 @@
     private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
     private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
 
+    private final AtomicBoolean mQuit = new AtomicBoolean(false);
+
     // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
     // limitations for (b/17379185).
     private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
@@ -325,7 +328,15 @@
             Log.d(TAG, "configureOutputs with " + outputsStr);
         }
 
-        stopPreview();
+        try {
+            stopPreview();
+        }  catch (RuntimeException e) {
+            Log.e(TAG, "Received device exception in configure call: ", e);
+            mDeviceState.setError(
+                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+            return;
+        }
+
         /*
          * Try to release the previous preview's surface texture earlier if we end up
          * using a different one; this also reduces the likelihood of getting into a deadlock
@@ -335,6 +346,11 @@
             mCamera.setPreviewTexture(/*surfaceTexture*/null);
         } catch (IOException e) {
             Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Received device exception in configure call: ", e);
+            mDeviceState.setError(
+                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+            return;
         }
 
         if (mGLThreadManager != null) {
@@ -470,7 +486,14 @@
             mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
         }
 
-        mCamera.setParameters(mParams);
+        try {
+            mCamera.setParameters(mParams);
+        } catch (RuntimeException e) {
+                Log.e(TAG, "Received device exception while configuring: ", e);
+                mDeviceState.setError(
+                        CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+
+        }
     }
 
     private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
@@ -793,7 +816,7 @@
                             }
 
                         } catch (IOException e) {
-                            Log.e(TAG, "Received device exception: ", e);
+                            Log.e(TAG, "Received device exception during capture call: ", e);
                             mDeviceState.setError(
                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
                             break;
@@ -802,6 +825,11 @@
                             mDeviceState.setError(
                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
                             break;
+                        } catch (RuntimeException e) {
+                            Log.e(TAG, "Received device exception during capture call: ", e);
+                            mDeviceState.setError(
+                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+                            break;
                         }
 
                         if (paramsChanged) {
@@ -878,9 +906,11 @@
                     }
                     if (mGLThreadManager != null) {
                         mGLThreadManager.quit();
+                        mGLThreadManager = null;
                     }
                     if (mCamera != null) {
                         mCamera.release();
+                        mCamera = null;
                     }
                     resetJpegSurfaceFormats(mCallbackOutputs);
                     break;
@@ -942,14 +972,16 @@
      * Quit the request thread, and clean up everything.
      */
     public void quit() {
-        Handler handler = mRequestThread.waitAndGetHandler();
-        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
-        mRequestThread.quitSafely();
-        try {
-            mRequestThread.join();
-        } catch (InterruptedException e) {
-            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
-                    mRequestThread.getName(), mRequestThread.getId()));
+        if (!mQuit.getAndSet(true)) {  // Avoid sending messages on dead thread's handler.
+            Handler handler = mRequestThread.waitAndGetHandler();
+            handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
+            mRequestThread.quitSafely();
+            try {
+                mRequestThread.join();
+            } catch (InterruptedException e) {
+                Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+                        mRequestThread.getName(), mRequestThread.getId()));
+            }
         }
     }
 
diff --git a/core/java/android/hardware/camera2/utils/ArrayUtils.java b/core/java/android/hardware/camera2/utils/ArrayUtils.java
index 5a78bbd..79a335c 100644
--- a/core/java/android/hardware/camera2/utils/ArrayUtils.java
+++ b/core/java/android/hardware/camera2/utils/ArrayUtils.java
@@ -117,7 +117,7 @@
 
             // Guard against unexpected values
             if (strIndex < 0) {
-                Log.w(TAG, "Ignoring invalid value " + str);
+                if (VERBOSE) Log.v(TAG, "Ignoring invalid value " + str);
                 continue;
             }
 
diff --git a/core/java/android/hardware/hdmi/HdmiRecordSources.java b/core/java/android/hardware/hdmi/HdmiRecordSources.java
index 922b8e7..7e94b89 100644
--- a/core/java/android/hardware/hdmi/HdmiRecordSources.java
+++ b/core/java/android/hardware/hdmi/HdmiRecordSources.java
@@ -759,6 +759,8 @@
      */
     @SystemApi
     public static boolean checkRecordSource(byte[] recordSource) {
+        if (recordSource == null || recordSource.length == 0) return false;
+
         int recordSourceType = recordSource[0];
         int extraDataSize = recordSource.length - 1;
         switch (recordSourceType) {
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index f64ef87..f283051 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -68,6 +68,8 @@
      * accessory function is enabled
      * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
      * audio source function is enabled
+     * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
+     * MIDI function is enabled
      * </ul>
      *
      * {@hide}
@@ -188,6 +190,14 @@
     public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source";
 
     /**
+     * Name of the MIDI USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     *
+     * {@hide}
+     */
+    public static final String USB_FUNCTION_MIDI = "midi";
+
+    /**
      * Name of the Accessory USB function.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      *
diff --git a/core/java/android/midi/MidiDevice.java b/core/java/android/midi/MidiDevice.java
index 8aaa86d..b91aedf 100644
--- a/core/java/android/midi/MidiDevice.java
+++ b/core/java/android/midi/MidiDevice.java
@@ -30,6 +30,7 @@
  * This class is used for sending and receiving data to and from an MIDI device
  * Instances of this class are created by {@link MidiManager#openDevice}.
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public final class MidiDevice {
diff --git a/core/java/android/midi/MidiDeviceInfo.java b/core/java/android/midi/MidiDeviceInfo.java
index 5cf62b5..dde2669 100644
--- a/core/java/android/midi/MidiDeviceInfo.java
+++ b/core/java/android/midi/MidiDeviceInfo.java
@@ -28,6 +28,7 @@
  * This class is just an immutable object to encapsulate the MIDI device description.
  * Use the MidiDevice class to actually communicate with devices.
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public class MidiDeviceInfo implements Parcelable {
@@ -45,7 +46,7 @@
     public static final int TYPE_VIRTUAL = 2;
 
     private final int mType;    // USB or virtual
-    private final int mId;  // unique ID generated by MidiService
+    private final int mId;      // unique ID generated by MidiService
     private final int mInputPortCount;
     private final int mOutputPortCount;
     private final Bundle mProperties;
diff --git a/core/java/android/midi/MidiDeviceServer.java b/core/java/android/midi/MidiDeviceServer.java
index ccb2e0c..7499934 100644
--- a/core/java/android/midi/MidiDeviceServer.java
+++ b/core/java/android/midi/MidiDeviceServer.java
@@ -25,7 +25,14 @@
 import java.io.IOException;
 import java.util.ArrayList;
 
-/** @hide */
+/**
+ * This class is used to provide the implemention of MIDI device.
+ * Applications may call {@link MidiManager#createDeviceServer}
+ * to create an instance of this class to implement a virtual MIDI device.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
 public final class MidiDeviceServer implements Closeable {
     private static final String TAG = "MidiDeviceServer";
 
diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java
index 5d806cf8..51c47dd 100644
--- a/core/java/android/midi/MidiInputPort.java
+++ b/core/java/android/midi/MidiInputPort.java
@@ -26,6 +26,7 @@
 /**
  * This class is used for sending data to a port on a MIDI device
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public class MidiInputPort extends MidiPort implements MidiReceiver {
@@ -33,7 +34,7 @@
     private final FileOutputStream mOutputStream;
 
     // buffer to use for sending messages out our output stream
-    private final byte[] mBuffer = new byte[MAX_PACKED_MESSAGE_SIZE];
+    private final byte[] mBuffer = new byte[MAX_PACKET_SIZE];
 
   /* package */ MidiInputPort(ParcelFileDescriptor pfd, int portNumber) {
         super(portNumber);
@@ -50,10 +51,19 @@
      *                  {@link java.lang.System#nanoTime}
      */
     public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException {
+        assert(offset >= 0 && count >= 0 && offset + count <= msg.length);
+
         synchronized (mBuffer) {
-            int length = packMessage(msg, offset, count, timestamp, mBuffer);
             try {
-                mOutputStream.write(mBuffer, 0, length);
+                while (count > 0) {
+                    int length = packMessage(msg, offset, count, timestamp, mBuffer);
+                    mOutputStream.write(mBuffer, 0, length);
+                    int sent = getMessageSize(mBuffer, length);
+                    assert(sent >= 0 && sent <= length);
+
+                    offset += sent;
+                    count -= sent;
+                }
             } catch (IOException e) {
                 IoUtils.closeQuietly(mOutputStream);
                 // report I/O failure
diff --git a/core/java/android/midi/MidiManager.java b/core/java/android/midi/MidiManager.java
index 2c1c7bf..8aa8395 100644
--- a/core/java/android/midi/MidiManager.java
+++ b/core/java/android/midi/MidiManager.java
@@ -35,6 +35,7 @@
  * {@samplecode
  * MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);}
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public class MidiManager {
@@ -184,7 +185,7 @@
      * @param properties a {@link android.os.Bundle} containing properties describing the device
      * @param isPrivate true if this device should only be visible and accessible to apps
      *                  with the same UID as the caller
-     * @return a {@link MidiVirtualDevice} object to locally represent the device
+     * @return a {@link MidiDeviceServer} object to locally represent the device
      */
     public MidiDeviceServer createDeviceServer(int numInputPorts, int numOutputPorts,
             Bundle properties, boolean isPrivate) {
diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java
index b550ed4..332b431 100644
--- a/core/java/android/midi/MidiOutputPort.java
+++ b/core/java/android/midi/MidiOutputPort.java
@@ -26,8 +26,9 @@
 import java.util.ArrayList;
 
 /**
- * This class is used for receiving data to a port on a MIDI device
+ * This class is used for receiving data from a port on a MIDI device
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public class MidiOutputPort extends MidiPort implements MidiSender {
@@ -45,7 +46,7 @@
     private final Thread mThread = new Thread() {
         @Override
         public void run() {
-            byte[] buffer = new byte[MAX_PACKED_MESSAGE_SIZE];
+            byte[] buffer = new byte[MAX_PACKET_SIZE];
             ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>();
 
             try {
@@ -54,9 +55,6 @@
                     int count = mInputStream.read(buffer);
                     if (count < 0) {
                         break;
-                    } else if (count < MIN_PACKED_MESSAGE_SIZE || count > MAX_PACKED_MESSAGE_SIZE) {
-                        Log.e(TAG, "Number of bytes read out of range: " + count);
-                        continue;
                     }
 
                     int offset = getMessageOffset(buffer, count);
@@ -88,15 +86,15 @@
                     }
                 }
             } catch (IOException e) {
-                Log.e(TAG, "read failed");
                 // report I/O failure
+                Log.e(TAG, "read failed");
+            } finally {
                 IoUtils.closeQuietly(mInputStream);
                 onIOException();
             }
         }
     };
 
-
   /* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) {
         super(portNumber);
         mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
diff --git a/core/java/android/midi/MidiPort.java b/core/java/android/midi/MidiPort.java
index 1fa7946..7512a90 100644
--- a/core/java/android/midi/MidiPort.java
+++ b/core/java/android/midi/MidiPort.java
@@ -24,6 +24,7 @@
  * This class represents a MIDI input or output port.
  * Base class for {@link MidiInputPort} and {@link MidiOutputPort}
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 abstract public class MidiPort implements Closeable {
@@ -32,16 +33,19 @@
     private final int mPortNumber;
 
     /**
-     * Minimum size of packed message as sent through our ParcelFileDescriptor
-     * 8 bytes for timestamp and 1 to 3 bytes for message
+     * Maximum size of a packet that can pass through our ParcelFileDescriptor
      */
-    protected static final int MIN_PACKED_MESSAGE_SIZE = 9;
+    protected static final int MAX_PACKET_SIZE = 1024;
 
     /**
-     * Maximum size of packed message as sent through our ParcelFileDescriptor
-     * 8 bytes for timestamp and 1 to 3 bytes for message
+     * size of message timestamp in bytes
      */
-    protected static final int MAX_PACKED_MESSAGE_SIZE = 11;
+    private static final int TIMESTAMP_SIZE = 8;
+
+    /**
+     * Maximum amount of MIDI data that can be included in a packet
+     */
+    public static final int MAX_PACKET_DATA_SIZE = MAX_PACKET_SIZE - TIMESTAMP_SIZE;
 
 
   /* package */ MidiPort(int portNumber) {
@@ -76,49 +80,52 @@
      */
     protected static int packMessage(byte[] message, int offset, int size, long timestamp,
             byte[] dest) {
-        // pack variable length message first
+        if (size + TIMESTAMP_SIZE > MAX_PACKET_SIZE) {
+            size = MAX_PACKET_SIZE - TIMESTAMP_SIZE;
+        }
+        // message data goes first
         System.arraycopy(message, offset, dest, 0, size);
-        int destOffset = size;
-        // timestamp takes 8 bytes
-        for (int i = 0; i < 8; i++) {
-            dest[destOffset++] = (byte)timestamp;
+
+        // followed by timestamp
+        for (int i = 0; i < TIMESTAMP_SIZE; i++) {
+            dest[size++] = (byte)timestamp;
             timestamp >>= 8;
         }
 
-        return destOffset;
+        return size;
     }
 
     /**
-     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
-     * returns the offet of of MIDI message in packed buffer
+     * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+     * returns the offset of the MIDI message in packed buffer
      */
     protected static int getMessageOffset(byte[] buffer, int bufferLength) {
-        // message is at start of buffer
+        // message is at the beginning
         return 0;
     }
 
     /**
-     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
-     * returns size of MIDI message in packed buffer
+     * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+     * returns size of MIDI data in packed buffer
      */
     protected static int getMessageSize(byte[] buffer, int bufferLength) {
-        // message length is total buffer length minus size of the timestamp and port number
-        return bufferLength - 8 /* sizeof(timestamp) */;
+        // message length is total buffer length minus size of the timestamp
+        return bufferLength - TIMESTAMP_SIZE;
     }
 
     /**
-     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
+     * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
      * unpacks timestamp from packed buffer
      */
     protected static long getMessageTimeStamp(byte[] buffer, int bufferLength) {
+        // timestamp is at end of the packet
+        int offset = bufferLength;
         long timestamp = 0;
 
-        // timestamp follows variable length message data
-        int dataLength = getMessageSize(buffer, bufferLength);
-        for (int i = dataLength + 7; i >= dataLength; i--) {
-            int b = (int)buffer[i] & 0xFF;
+        for (int i = 0; i < TIMESTAMP_SIZE; i++) {
+            int b = (int)buffer[--offset] & 0xFF;
             timestamp = (timestamp << 8) | b;
         }
         return timestamp;
-     }
+    }
 }
diff --git a/core/java/android/midi/MidiReceiver.java b/core/java/android/midi/MidiReceiver.java
index 0b183cc..fdfe51a 100644
--- a/core/java/android/midi/MidiReceiver.java
+++ b/core/java/android/midi/MidiReceiver.java
@@ -19,21 +19,24 @@
 import java.io.IOException;
 
 /**
- * Interface for receiving events from a MIDI device.
+ * Interface for receiving data from a MIDI device.
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public interface MidiReceiver {
     /**
-     * Called to pass a MIDI event to the receiver.
+     * Called to pass MIDI data to the receiver.
      *
      * NOTE: the msg array parameter is only valid within the context of this call.
      * The msg bytes should be copied by the receiver rather than retaining a reference
      * to this parameter.
+     * Also, modifying the contents of the msg array parameter may result in other receivers
+     * in the same application receiving incorrect values in their onPost() method.
      *
-     * @param msg a byte array containing the MIDI message
-     * @param offset the offset of the first byte of the message in the byte array
-     * @param count the number of bytes in the message
+     * @param msg a byte array containing the MIDI data
+     * @param offset the offset of the first byte of the data in the byte array
+     * @param count the number of bytes of MIDI data in the array
      * @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime}
      * @throws IOException
      */
diff --git a/core/java/android/midi/MidiSender.java b/core/java/android/midi/MidiSender.java
index 7958a06..2b7afad 100644
--- a/core/java/android/midi/MidiSender.java
+++ b/core/java/android/midi/MidiSender.java
@@ -20,6 +20,7 @@
  * Interface provided by a device to allow attaching
  * MidiReceivers to a MIDI device.
  *
+ * CANDIDATE FOR PUBLIC API
  * @hide
  */
 public interface MidiSender {
diff --git a/core/java/android/midi/MidiUtils.java b/core/java/android/midi/MidiUtils.java
deleted file mode 100644
index e60e2db..0000000
--- a/core/java/android/midi/MidiUtils.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.midi;
-
-import android.util.Log;
-
-/**
- * Class containing miscellaneous MIDI utilities.
- *
- * @hide
- */
-public final class MidiUtils {
-    private static final String TAG = "MidiUtils";
-
-    private MidiUtils() { }
-
-    /**
-     * Returns data size of a MIDI message based on the message's command byte
-     * @param b the message command byte
-     * @return the message's data length
-     */
-    public static int getMessageDataSize(byte b) {
-        switch (b & 0xF0) {
-            case 0x80:
-            case 0x90:
-            case 0xA0:
-            case 0xB0:
-            case 0xE0:
-                return 2;
-            case 0xC0:
-            case 0xD0:
-                return 1;
-            case 0xF0:
-                switch (b & 0x0F) {
-                    case 0x00:
-                        Log.e(TAG, "System Exclusive not supported yet");
-                        return -1;
-                    case 0x01:
-                    case 0x03:
-                        return 1;
-                    case 0x02:
-                        return 2;
-                    default:
-                        return 0;
-                }
-            default:
-                Log.e(TAG, "unknown MIDI command " + b);
-                return -1;
-        }
-    }
-}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 6654577..27096b1 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -159,6 +159,8 @@
      *     instead. The Apache HTTP client is no longer maintained and may be removed in a future
      *     release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
      *     for further details.
+     *
+     * @removed
      */
     @Deprecated
     public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 365f2b6..37ee961 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -188,6 +188,7 @@
         for (InetAddress dnsServer : dnsServers) {
             NetworkUtils.parcelInetAddress(dest, dnsServer, flags);
         }
+        dest.writeString(domains);
     }
 
     protected static void readFromParcel(StaticIpConfiguration s, Parcel in) {
@@ -198,5 +199,6 @@
         for (int i = 0; i < size; i++) {
             s.dnsServers.add(NetworkUtils.unparcelInetAddress(in));
         }
+        s.domains = in.readString();
     }
 }
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 2099c3f..bf3d9aa 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -366,6 +366,7 @@
     public String toSafeString() {
         String scheme = getScheme();
         String ssp = getSchemeSpecificPart();
+        String authority = null;
         if (scheme != null) {
             if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip")
                     || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto")
@@ -384,6 +385,9 @@
                     }
                 }
                 return builder.toString();
+            } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
+                ssp = null;
+                authority = "//" + getAuthority() + "/...";
             }
         }
         // Not a sensitive scheme, but let's still be conservative about
@@ -397,6 +401,9 @@
         if (ssp != null) {
             builder.append(ssp);
         }
+        if (authority != null) {
+            builder.append(authority);
+        }
         return builder.toString();
     }
 
@@ -1742,7 +1749,7 @@
      *
      * @return normalized Uri (never null)
      * @see {@link android.content.Intent#setData}
-     * @see {@link #setNormalizedData}
+     * @see {@link android.content.Intent#setDataAndNormalize}
      */
     public Uri normalizeScheme() {
         String scheme = getScheme();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 2dfd919..e4b594a 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -597,6 +597,11 @@
          * Lollipop with an extra sugar coating on the outside!
          */
         public static final int LOLLIPOP_MR1 = 22;
+
+        /**
+         * M comes after L.
+         */
+        public static final int MNC = CUR_DEVELOPMENT;
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/IProcessInfoService.aidl b/core/java/android/os/IProcessInfoService.aidl
new file mode 100644
index 0000000..c98daa2
--- /dev/null
+++ b/core/java/android/os/IProcessInfoService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IProcessInfoService
+{
+    /**
+     * For each PID in the given input array, write the current process state
+     * for that process into the output array, or ActivityManager.PROCESS_STATE_NONEXISTENT
+     * to indicate that no process with the given PID exists.
+     */
+    void getProcessStatesFromPids(in int[] pids, out int[] states);
+}
+
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index bedc695..3d5215b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -276,7 +276,7 @@
 
     private static native byte[] nativeMarshall(long nativePtr);
     private static native void nativeUnmarshall(
-            long nativePtr, byte[] data, int offest, int length);
+            long nativePtr, byte[] data, int offset, int length);
     private static native void nativeAppendFrom(
             long thisNativePtr, long otherNativePtr, int offset, int length);
     private static native boolean nativeHasFileDescriptors(long nativePtr);
@@ -438,8 +438,8 @@
     /**
      * Set the bytes in data to be the raw bytes of this Parcel.
      */
-    public final void unmarshall(byte[] data, int offest, int length) {
-        nativeUnmarshall(mNativePtr, data, offest, length);
+    public final void unmarshall(byte[] data, int offset, int length) {
+        nativeUnmarshall(mNativePtr, data, offset, length);
     }
 
     public final void appendFrom(Parcel parcel, int offset, int length) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 8307d9b..d52dd30 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -835,6 +835,21 @@
     }
 
     /**
+     * Turn off the device.
+     *
+     * @param confirm If true, shows a shutdown confirmation dialog.
+     * @param wait If true, this call waits for the shutdown to complete and does not return.
+     *
+     * @hide
+     */
+    public void shutdown(boolean confirm, boolean wait) {
+        try {
+            mService.shutdown(confirm, wait);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
      * This broadcast is only sent to registered receivers.
      */
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index 30f0c6a..90d30d6 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -45,11 +45,22 @@
     private static final int VALID_COLOR_MODES =
             COLOR_MODE_MONOCHROME | COLOR_MODE_COLOR;
 
+    /** Duplex mode: No duplexing. */
+    public static final int DUPLEX_MODE_NONE = 1 << 0;
+    /** Duplex mode: Pages are turned sideways along the long edge - like a book. */
+    public static final int DUPLEX_MODE_LONG_EDGE = 1 << 1;
+    /** Duplex mode: Pages are turned upwards along the short edge - like a notpad. */
+    public static final int DUPLEX_MODE_SHORT_EDGE = 1 << 2;
+
+    private static final int VALID_DUPLEX_MODES =
+            DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
+
     private MediaSize mMediaSize;
     private Resolution mResolution;
     private Margins mMinMargins;
 
     private int mColorMode;
+    private int mDuplexMode = DUPLEX_MODE_NONE;
 
     PrintAttributes() {
         /* hide constructor */
@@ -60,6 +71,7 @@
         mResolution = (parcel.readInt() ==  1) ? Resolution.createFromParcel(parcel) : null;
         mMinMargins = (parcel.readInt() ==  1) ? Margins.createFromParcel(parcel) : null;
         mColorMode = parcel.readInt();
+        mDuplexMode = parcel.readInt();
     }
 
     /**
@@ -74,7 +86,7 @@
     /**
      * Sets the media size.
      *
-     * @param The media size.
+     * @param mediaSize The media size.
      *
      * @hide
      */
@@ -94,7 +106,7 @@
     /**
      * Sets the resolution.
      *
-     * @param The resolution.
+     * @param resolution The resolution.
      *
      * @hide
      */
@@ -130,7 +142,7 @@
      * </strong>
      * </p>
      *
-     * @param The margins.
+     * @param margins The margins.
      *
      * @hide
      */
@@ -153,7 +165,7 @@
     /**
      * Sets the color mode.
      *
-     * @param The color mode.
+     * @param colorMode The color mode.
      *
      * @see #COLOR_MODE_MONOCHROME
      * @see #COLOR_MODE_COLOR
@@ -179,6 +191,35 @@
     }
 
     /**
+     * Gets the duplex mode.
+     *
+     * @return The duplex mode.
+     *
+     * @see #DUPLEX_MODE_NONE
+     * @see #DUPLEX_MODE_LONG_EDGE
+     * @see #DUPLEX_MODE_SHORT_EDGE
+     */
+    public int getDuplexMode() {
+        return mDuplexMode;
+    }
+
+    /**
+     * Sets the duplex mode.
+     *
+     * @param duplexMode The duplex mode.
+     *
+     * @see #DUPLEX_MODE_NONE
+     * @see #DUPLEX_MODE_LONG_EDGE
+     * @see #DUPLEX_MODE_SHORT_EDGE
+     *
+     * @hide
+     */
+    public void setDuplexMode(int duplexMode) {
+        enforceValidDuplexMode(duplexMode);
+        mDuplexMode = duplexMode;
+    }
+
+    /**
      * Gets a new print attributes instance which is in portrait orientation,
      * which is the media size is in portrait and all orientation dependent
      * attributes such as resolution and margins are properly adjusted.
@@ -211,6 +252,7 @@
         attributes.setMinMargins(getMinMargins());
 
         attributes.setColorMode(getColorMode());
+        attributes.setDuplexMode(getDuplexMode());
 
         return attributes;
     }
@@ -248,6 +290,7 @@
         attributes.setMinMargins(getMinMargins());
 
         attributes.setColorMode(getColorMode());
+        attributes.setDuplexMode(getDuplexMode());
 
         return attributes;
     }
@@ -273,6 +316,7 @@
             parcel.writeInt(0);
         }
         parcel.writeInt(mColorMode);
+        parcel.writeInt(mDuplexMode);
     }
 
     @Override
@@ -285,6 +329,7 @@
         final int prime = 31;
         int result = 1;
         result = prime * result + mColorMode;
+        result = prime * result + mDuplexMode;
         result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
         result = prime * result + ((mMediaSize == null) ? 0 : mMediaSize.hashCode());
         result = prime * result + ((mResolution == null) ? 0 : mResolution.hashCode());
@@ -306,6 +351,9 @@
         if (mColorMode != other.mColorMode) {
             return false;
         }
+        if (mDuplexMode != other.mDuplexMode) {
+            return false;
+        }
         if (mMinMargins == null) {
             if (other.mMinMargins != null) {
                 return false;
@@ -344,6 +392,7 @@
         builder.append(", resolution: ").append(mResolution);
         builder.append(", minMargins: ").append(mMinMargins);
         builder.append(", colorMode: ").append(colorModeToString(mColorMode));
+        builder.append(", duplexMode: ").append(duplexModeToString(mDuplexMode));
         builder.append("}");
         return builder.toString();
     }
@@ -354,6 +403,7 @@
         mResolution = null;
         mMinMargins = null;
         mColorMode = 0;
+        mDuplexMode = DUPLEX_MODE_NONE;
     }
 
     /**
@@ -364,6 +414,7 @@
         mResolution = other.mResolution;
         mMinMargins = other.mMinMargins;
         mColorMode = other.mColorMode;
+        mDuplexMode = other.mDuplexMode;
     }
 
     /**
@@ -1270,17 +1321,41 @@
             case COLOR_MODE_COLOR: {
                 return "COLOR_MODE_COLOR";
             }
-            default:
+            default: {
                 return "COLOR_MODE_UNKNOWN";
+            }
+        }
+    }
+
+    static String duplexModeToString(int duplexMode) {
+        switch (duplexMode) {
+            case DUPLEX_MODE_NONE: {
+                return "DUPLEX_MODE_NONE";
+            }
+            case DUPLEX_MODE_LONG_EDGE: {
+                return "DUPLEX_MODE_LONG_EDGE";
+            }
+            case DUPLEX_MODE_SHORT_EDGE: {
+                return "DUPLEX_MODE_SHORT_EDGE";
+            }
+            default: {
+                return "DUPLEX_MODE_UNKNOWN";
+            }
         }
     }
 
     static void enforceValidColorMode(int colorMode) {
-        if ((colorMode & VALID_COLOR_MODES) == 0 && Integer.bitCount(colorMode) == 1) {
+        if ((colorMode & VALID_COLOR_MODES) == 0 || Integer.bitCount(colorMode) != 1) {
             throw new IllegalArgumentException("invalid color mode: " + colorMode);
         }
     }
 
+    static void enforceValidDuplexMode(int duplexMode) {
+        if ((duplexMode & VALID_DUPLEX_MODES) == 0 || Integer.bitCount(duplexMode) != 1) {
+            throw new IllegalArgumentException("invalid duplex mode: " + duplexMode);
+        }
+    }
+
     /**
      * Builder for creating {@link PrintAttributes}.
      */
@@ -1331,15 +1406,31 @@
          * @see PrintAttributes#COLOR_MODE_COLOR
          */
         public Builder setColorMode(int colorMode) {
-            if (Integer.bitCount(colorMode) > 1) {
-                throw new IllegalArgumentException("can specify at most one colorMode bit.");
-            }
             mAttributes.setColorMode(colorMode);
             return this;
         }
 
         /**
+         * Sets the duplex mode.
+         *
+         * @param duplexMode A valid duplex mode or zero.
+         * @return This builder.
+         *
+         * @see PrintAttributes#DUPLEX_MODE_NONE
+         * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+         * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+         */
+        public Builder setDuplexMode(int duplexMode) {
+            mAttributes.setDuplexMode(duplexMode);
+            return this;
+        }
+
+        /**
          * Creates a new {@link PrintAttributes} instance.
+         * <p>
+         * If you do not specify a duplex mode, the default
+         * {@link #DUPLEX_MODE_NONE} will be used.
+         * </p>
          *
          * @return The new instance.
          */
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index 806a89d8..96f3185 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -47,7 +47,8 @@
     private static final int PROPERTY_MEDIA_SIZE = 0;
     private static final int PROPERTY_RESOLUTION = 1;
     private static final int PROPERTY_COLOR_MODE = 2;
-    private static final int PROPERTY_COUNT = 3;
+    private static final int PROPERTY_DUPLEX_MODE = 3;
+    private static final int PROPERTY_COUNT = 4;
 
     private static final Margins DEFAULT_MARGINS = new Margins(0,  0,  0,  0);
 
@@ -56,6 +57,7 @@
     private List<Resolution> mResolutions;
 
     private int mColorModes;
+    private int mDuplexModes;
 
     private final int[] mDefaults = new int[PROPERTY_COUNT];
 
@@ -106,6 +108,7 @@
         }
 
         mColorModes = other.mColorModes;
+        mDuplexModes = other.mDuplexModes;
 
         final int defaultCount = other.mDefaults.length;
         for (int i = 0; i < defaultCount; i++) {
@@ -154,6 +157,19 @@
     }
 
     /**
+     * Gets the bit mask of supported duplex modes.
+     *
+     * @return The bit mask of supported duplex modes.
+     *
+     * @see PrintAttributes#DUPLEX_MODE_NONE
+     * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+     * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+     */
+    public int getDuplexModes() {
+        return mDuplexModes;
+    }
+
+    /**
      * Gets the default print attributes.
      *
      * @return The default attributes.
@@ -178,6 +194,11 @@
             builder.setColorMode(colorMode);
         }
 
+        final int duplexMode = mDefaults[PROPERTY_DUPLEX_MODE];
+        if (duplexMode > 0) {
+            builder.setDuplexMode(duplexMode);
+        }
+
         return builder.build();
     }
 
@@ -187,6 +208,7 @@
         readResolutions(parcel);
 
         mColorModes = parcel.readInt();
+        mDuplexModes = parcel.readInt();
 
         readDefaults(parcel);
     }
@@ -203,6 +225,7 @@
         writeResolutions(parcel);
 
         parcel.writeInt(mColorModes);
+        parcel.writeInt(mDuplexModes);
 
         writeDefaults(parcel);
     }
@@ -215,6 +238,7 @@
         result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode());
         result = prime * result + ((mResolutions == null) ? 0 : mResolutions.hashCode());
         result = prime * result + mColorModes;
+        result = prime * result + mDuplexModes;
         result = prime * result + Arrays.hashCode(mDefaults);
         return result;
     }
@@ -255,6 +279,9 @@
         if (mColorModes != other.mColorModes) {
             return false;
         }
+        if (mDuplexModes != other.mDuplexModes) {
+            return false;
+        }
         if (!Arrays.equals(mDefaults, other.mDefaults)) {
             return false;
         }
@@ -269,6 +296,7 @@
         builder.append(", mediaSizes=").append(mMediaSizes);
         builder.append(", resolutions=").append(mResolutions);
         builder.append(", colorModes=").append(colorModesToString());
+        builder.append(", duplexModes=").append(duplexModesToString());
         builder.append("\"}");
         return builder.toString();
     }
@@ -289,6 +317,22 @@
         return builder.toString();
     }
 
+    private String duplexModesToString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append('[');
+        int duplexModes = mDuplexModes;
+        while (duplexModes != 0) {
+            final int duplexMode = 1 << Integer.numberOfTrailingZeros(duplexModes);
+            duplexModes &= ~duplexMode;
+            if (builder.length() > 1) {
+                builder.append(", ");
+            }
+            builder.append(PrintAttributes.duplexModeToString(duplexMode));
+        }
+        builder.append(']');
+        return builder.toString();
+    }
+
     private void writeMediaSizes(Parcel parcel) {
         if (mMediaSizes == null) {
             parcel.writeInt(0);
@@ -495,19 +539,51 @@
                 currentModes &= ~currentMode;
                 PrintAttributes.enforceValidColorMode(currentMode);
             }
-            if ((colorModes & defaultColorMode) == 0) {
-                throw new IllegalArgumentException("Default color mode not in color modes.");
-            }
-            PrintAttributes.enforceValidColorMode(colorModes);
+            PrintAttributes.enforceValidColorMode(defaultColorMode);
             mPrototype.mColorModes = colorModes;
             mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
             return this;
         }
 
         /**
+         * Sets the duplex modes.
+         * <p>
+         * <strong>Required:</strong> No
+         * </p>
+         *
+         * @param duplexModes The duplex mode bit mask.
+         * @param defaultDuplexMode The default duplex mode.
+         * @return This builder.
+         *
+         * @throws IllegalArgumentException If duplex modes contains an invalid
+         *         mode bit or if the default duplex mode is invalid.
+         *
+         * @see PrintAttributes#DUPLEX_MODE_NONE
+         * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+         * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+         */
+        public Builder setDuplexModes(int duplexModes, int defaultDuplexMode) {
+            int currentModes = duplexModes;
+            while (currentModes > 0) {
+                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
+                currentModes &= ~currentMode;
+                PrintAttributes.enforceValidDuplexMode(currentMode);
+            }
+            PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
+            mPrototype.mDuplexModes = duplexModes;
+            mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode;
+            return this;
+        }
+
+        /**
          * Crates a new {@link PrinterCapabilitiesInfo} enforcing that all
          * required properties have been specified. See individual methods
          * in this class for reference about required attributes.
+         * <p>
+         * <strong>Note:</strong> If you do not add supported duplex modes,
+         * {@link android.print.PrintAttributes#DUPLEX_MODE_NONE} will set
+         * as the only supported mode and also as the default duplex mode.
+         * </p>
          *
          * @return A new {@link PrinterCapabilitiesInfo}.
          *
@@ -532,6 +608,10 @@
             if (mPrototype.mDefaults[PROPERTY_COLOR_MODE] == DEFAULT_UNDEFINED) {
                 throw new IllegalStateException("No default color mode specified.");
             }
+            if (mPrototype.mDuplexModes == 0) {
+                setDuplexModes(PrintAttributes.DUPLEX_MODE_NONE,
+                        PrintAttributes.DUPLEX_MODE_NONE);
+            }
             if (mPrototype.mMinMargins == null) {
                 throw new IllegalArgumentException("margins cannot be null");
             }
@@ -558,4 +638,3 @@
         }
     };
 }
-
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index ae95854..7ed1649 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -192,7 +192,7 @@
 
     /**
      * If you declared an optional activity with advanced print options via the
-     * {@link R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity}
+     * {@link android.R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity}
      * attribute, this extra is used to pass in the currently constructed {@link
      * PrintJobInfo} to your activity allowing you to modify it. After you are
      * done, you must return the modified {@link PrintJobInfo} via the same extra.
@@ -224,7 +224,7 @@
 
     /**
      * If you declared an optional activity with advanced print options via the
-     * {@link R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity}
+     * {@link android.R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity}
      * attribute, this extra is used to pass in the currently selected printer's
      * {@link android.print.PrinterInfo} to your activity allowing you to inspect it.
      *
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 7dd559c..67ac043 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1434,9 +1434,9 @@
         * and {@link #CONTENT_MULTI_VCARD_URI} to indicate that the returned
         * vcard should not contain a photo.
         *
-        * @hide
+        * This is useful for obtaining a space efficient vcard.
         */
-        public static final String QUERY_PARAMETER_VCARD_NO_PHOTO = "nophoto";
+        public static final String QUERY_PARAMETER_VCARD_NO_PHOTO = "no_photo";
 
         /**
          * Base {@link Uri} for referencing multiple {@link Contacts} entry,
@@ -2194,6 +2194,16 @@
         public static final String CONTACT_ID = "contact_id";
 
         /**
+         * Persistent unique id for each raw_contact within its account.
+         * This id is provided by its own data source, and can be used to backup metadata
+         * to the server.
+         * This should be unique within each set of account_name/account_type/data_set
+         *
+         * @hide
+         */
+        public static final String BACKUP_ID = "backup_id";
+
+        /**
          * The data set within the account that this row belongs to.  This allows
          * multiple sync adapters for the same account type to distinguish between
          * each others' data.
@@ -2238,33 +2248,6 @@
         public static final String DELETED = "deleted";
 
         /**
-         * The "name_verified" flag: "1" means that the name fields on this raw
-         * contact can be trusted and therefore should be used for the entire
-         * aggregated contact.
-         * <p>
-         * If an aggregated contact contains more than one raw contact with a
-         * verified name, one of those verified names is chosen at random.
-         * If an aggregated contact contains no verified names, the
-         * name is chosen randomly from the constituent raw contacts.
-         * </p>
-         * <p>
-         * Updating this flag from "0" to "1" automatically resets it to "0" on
-         * all other raw contacts in the same aggregated contact.
-         * </p>
-         * <p>
-         * Sync adapters should only specify a value for this column when
-         * inserting a raw contact and leave it out when doing an update.
-         * </p>
-         * <p>
-         * The default value is "0"
-         * </p>
-         * <p>Type: INTEGER</p>
-         *
-         * @hide
-         */
-        public static final String NAME_VERIFIED = "name_verified";
-
-        /**
          * The "read-only" flag: "0" by default, "1" if the row cannot be modified or
          * deleted except by a sync adapter.  See {@link ContactsContract#CALLER_IS_SYNCADAPTER}.
          * <P>Type: INTEGER</P>
@@ -2960,7 +2943,6 @@
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DELETED);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, NAME_VERIFIED);
                 android.content.Entity contact = new android.content.Entity(cv);
 
                 // read data rows until the contact id changes
@@ -3986,6 +3968,13 @@
         public static final String MIMETYPE = "mimetype";
 
         /**
+         * Hash id on the data fields, used for backup and restore.
+         *
+         * @hide
+         */
+        public static final String HASH_ID = "hash_id";
+
+        /**
          * A reference to the {@link RawContacts#_ID}
          * that this data belongs to.
          */
@@ -8072,8 +8061,10 @@
         public static final String EXTRA_TARGET_RECT = "android.provider.extra.TARGET_RECT";
 
         /**
-         * Extra used to specify size of pivot dialog.
-         * @hide
+         * Extra used to specify size of QuickContacts. Not all implementations of QuickContacts
+         * will respect this extra's value.
+         *
+         * One of {@link #MODE_SMALL}, {@link #MODE_MEDIUM}, or {@link #MODE_LARGE}.
          */
         public static final String EXTRA_MODE = "android.provider.extra.MODE";
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e055203..dddbe78 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.app.SearchManager;
 import android.app.WallpaperManager;
 import android.content.ComponentName;
@@ -5104,6 +5105,7 @@
          * Whether Theater Mode is on.
          * {@hide}
          */
+        @SystemApi
         public static final String THEATER_MODE_ON = "theater_mode_on";
 
         /**
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2b53c48..b84c3aa 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1754,8 +1754,8 @@
 
     private char getEllipsisChar(TextUtils.TruncateAt method) {
         return (method == TextUtils.TruncateAt.END_SMALL) ?
-                ELLIPSIS_TWO_DOTS[0] :
-                ELLIPSIS_NORMAL[0];
+                TextUtils.ELLIPSIS_TWO_DOTS[0] :
+                TextUtils.ELLIPSIS_NORMAL[0];
     }
 
     private void ellipsize(int start, int end, int line,
@@ -1952,6 +1952,4 @@
     /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT =
         new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
 
-    /* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
-    /* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5b07397..2d4b4dc 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -554,7 +554,7 @@
 
         float ellipsisWidth = paint.measureText(
                 (where == TextUtils.TruncateAt.END_SMALL) ?
-                        ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL, 0, 1);
+                        TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
         int ellipsisStart = 0;
         int ellipsisCount = 0;
         int len = lineEnd - lineStart;
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 8a8c6d8..48bb5dd 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -63,9 +63,12 @@
 
 public class TextUtils {
     private static final String TAG = "TextUtils";
-    private static final String ELLIPSIS = new String(Layout.ELLIPSIS_NORMAL);
-    private static final String ELLIPSIS_TWO_DOTS = new String(Layout.ELLIPSIS_TWO_DOTS);
 
+    /* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
+    private static final String ELLIPSIS_STRING = new String(ELLIPSIS_NORMAL);
+
+    /* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
+    private static final String ELLIPSIS_TWO_DOTS_STRING = new String(ELLIPSIS_TWO_DOTS);
 
     private TextUtils() { /* cannot be instantiated */ }
 
@@ -1085,7 +1088,7 @@
                                          EllipsizeCallback callback) {
         return ellipsize(text, paint, avail, where, preserveLength, callback,
                 TextDirectionHeuristics.FIRSTSTRONG_LTR,
-                (where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS);
+                (where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS_STRING : ELLIPSIS_STRING);
     }
 
     /**
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 7bd6287..80245ef 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -268,7 +268,12 @@
         @Override
         public boolean onPreDraw() {
             removeListeners();
-            sPendingTransitions.remove(mSceneRoot);
+
+            // Don't start the transition if it's no longer pending.
+            if (!sPendingTransitions.remove(mSceneRoot)) {
+                return true;
+            }
+
             // Add to running list, handle end to remove it
             final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
                     getRunningTransitions();
@@ -417,4 +422,24 @@
             sceneChangeRunTransition(sceneRoot, transitionClone);
         }
     }
+
+    /**
+     * Ends all pending and ongoing transitions on the specified scene root.
+     *
+     * @param sceneRoot The root of the View hierarchy to end transitions on.
+     * @hide
+     */
+    public static void endTransitions(final ViewGroup sceneRoot) {
+        sPendingTransitions.remove(sceneRoot);
+
+        final ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
+        if (runningTransitions != null) {
+            final int count = runningTransitions.size();
+            for (int i = 0; i < count; i++) {
+                final Transition transition = runningTransitions.get(i);
+                transition.end();
+            }
+        }
+
+    }
 }
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 7c9861f..6ed3885 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -255,7 +255,10 @@
     }
 
     private ArrayMap(boolean immutable) {
-        mHashes = EmptyArray.INT;
+        // If this is immutable, use the sentinal EMPTY_IMMUTABLE_INTS
+        // instance instead of the usual EmptyArray.INT. The reference
+        // is checked later to see if the array is allowed to grow.
+        mHashes = immutable ? EMPTY_IMMUTABLE_INTS : EmptyArray.INT;
         mArray = EmptyArray.OBJECT;
         mSize = 0;
     }
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 5e05683..68ad782 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -636,95 +636,6 @@
         }
     }
 
-    public void computeClickPointInScreenClientThread(long accessibilityNodeId,
-            Region interactiveRegion, int interactionId,
-            IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
-            long interrogatingTid, MagnificationSpec spec) {
-        Message message = mHandler.obtainMessage();
-        message.what = PrivateHandler.MSG_COMPUTE_CLICK_POINT_IN_SCREEN;
-
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
-        args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
-        args.argi3 = interactionId;
-        args.arg1 = callback;
-        args.arg2 = spec;
-        args.arg3 = interactiveRegion;
-
-        message.obj = args;
-
-        // If the interrogation is performed by the same thread as the main UI
-        // thread in this process, set the message as a static reference so
-        // after this call completes the same thread but in the interrogating
-        // client can handle the message to generate the result.
-        if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
-            AccessibilityInteractionClient.getInstanceForThread(
-                    interrogatingTid).setSameThreadMessage(message);
-        } else {
-            mHandler.sendMessage(message);
-        }
-    }
-
-    private void computeClickPointInScreenUiThread(Message message) {
-        SomeArgs args = (SomeArgs) message.obj;
-        final int accessibilityViewId = args.argi1;
-        final int virtualDescendantId = args.argi2;
-        final int interactionId = args.argi3;
-        final IAccessibilityInteractionConnectionCallback callback =
-                (IAccessibilityInteractionConnectionCallback) args.arg1;
-        final MagnificationSpec spec = (MagnificationSpec) args.arg2;
-        final Region interactiveRegion = (Region) args.arg3;
-        args.recycle();
-
-        boolean succeeded = false;
-        Point point = mTempPoint;
-        try {
-            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
-                return;
-            }
-            View target = null;
-            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                target = findViewByAccessibilityId(accessibilityViewId);
-            } else {
-                target = mViewRootImpl.mView;
-            }
-            if (target != null && isShown(target)) {
-                AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
-                if (provider != null) {
-                    // For virtual views just use the center of the bounds in screen.
-                    AccessibilityNodeInfo node = null;
-                    if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                        node = provider.createAccessibilityNodeInfo(virtualDescendantId);
-                    } else {
-                        node = provider.createAccessibilityNodeInfo(
-                                AccessibilityNodeProvider.HOST_VIEW_ID);
-                    }
-                    if (node != null) {
-                        succeeded = true;
-                        Rect boundsInScreen = mTempRect;
-                        node.getBoundsInScreen(boundsInScreen);
-                        point.set(boundsInScreen.centerX(), boundsInScreen.centerY());
-                    }
-                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                    // For a real view, ask the view to compute the click point.
-                    succeeded = target.computeClickPointInScreenForAccessibility(
-                            interactiveRegion, point);
-                }
-            }
-        } finally {
-            try {
-                Point result = null;
-                if (succeeded) {
-                    applyAppScaleAndMagnificationSpecIfNeeded(point, spec);
-                    result = point;
-                }
-                callback.setComputeClickPointInScreenActionResult(result, interactionId);
-            } catch (RemoteException re) {
-                /* ignore - the other side will time out */
-            }
-        }
-    }
-
     private View findViewByAccessibilityId(int accessibilityId) {
         View root = mViewRootImpl.mView;
         if (root == null) {
@@ -1201,7 +1112,6 @@
         private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
         private final static int MSG_FIND_FOCUS = 5;
         private final static int MSG_FOCUS_SEARCH = 6;
-        private final static int MSG_COMPUTE_CLICK_POINT_IN_SCREEN = 7;
 
         public PrivateHandler(Looper looper) {
             super(looper);
@@ -1223,8 +1133,6 @@
                     return "MSG_FIND_FOCUS";
                 case MSG_FOCUS_SEARCH:
                     return "MSG_FOCUS_SEARCH";
-                case MSG_COMPUTE_CLICK_POINT_IN_SCREEN:
-                    return "MSG_COMPUTE_CLICK_POINT_IN_SCREEN";
                 default:
                     throw new IllegalArgumentException("Unknown message type: " + type);
             }
@@ -1252,9 +1160,6 @@
                 case MSG_FOCUS_SEARCH: {
                     focusSearchUiThread(message);
                 } break;
-                case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: {
-                    computeClickPointInScreenUiThread(message);
-                } break;
                 default:
                     throw new IllegalArgumentException("Unknown message type: " + type);
             }
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index a359952..ae4b60f 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -29,8 +29,21 @@
  * </div>
  */
 public abstract class ActionMode {
+
+    /**
+     * The action mode is treated as a Primary mode. This is the default.
+     * Use with {@link #setType}.
+     */
+    public static final int TYPE_PRIMARY = 0;
+    /**
+     * The action mode is treated as a Floating Toolbar.
+     * Use with {@link #setType}.
+     */
+    public static final int TYPE_FLOATING = 1;
+
     private Object mTag;
     private boolean mTitleOptionalHint;
+    private int mType = TYPE_PRIMARY;
 
     /**
      * Set a tag object associated with this ActionMode.
@@ -154,6 +167,25 @@
     public abstract void setCustomView(View view);
 
     /**
+     * Set a type for this action mode. This will affect how the system renders the action mode if
+     * it has to.
+     *
+     * @param type One of {@link #TYPE_PRIMARY} or {@link #TYPE_FLOATING}.
+     */
+    public void setType(int type) {
+        mType = type;
+    }
+
+    /**
+     * Returns the type for this action mode.
+     *
+     * @return One of {@link #TYPE_PRIMARY} or {@link #TYPE_FLOATING}.
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
      * Invalidate the action mode and refresh menu content. The mode's
      * {@link ActionMode.Callback} will have its
      * {@link Callback#onPrepareActionMode(ActionMode, Menu)} method called.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7b20e72..743f6b7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -81,7 +81,7 @@
     void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind);
-    void setAppGroupId(IBinder token, int groupId);
+    void setAppTask(IBinder token, int taskId);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
     void setFocusedApp(IBinder token, boolean moveFocusNow);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7b13e84..d08ab46 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -188,9 +188,6 @@
 
     void wallpaperCommandComplete(IBinder window, in Bundle result);
 
-    void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
-            float dsdx, float dtdx, float dsdy, float dtdy);
-
     /**
      * Notifies that a rectangle on the screen has been requested.
      */
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index ac32430..a5225cb 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -16,24 +16,25 @@
 
 package android.view;
 
-import android.annotation.Nullable;
-import android.graphics.Canvas;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Trace;
-import android.util.TypedValue;
-import android.widget.FrameLayout;
+import com.android.internal.R;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.graphics.Canvas;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.util.Xml;
+import android.widget.FrameLayout;
 
 import java.io.IOException;
 import java.lang.reflect.Constructor;
@@ -925,6 +926,14 @@
                                 inheritContext);
                         final ViewGroup group = (ViewGroup) parent;
 
+                        final TypedArray a = context.obtainStyledAttributes(
+                                attrs, R.styleable.Include);
+                        final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
+                        final int visibility = a.getInt(R.styleable.Include_visibility, -1);
+                        final boolean hasWidth = a.hasValue(R.styleable.Include_layout_width);
+                        final boolean hasHeight = a.hasValue(R.styleable.Include_layout_height);
+                        a.recycle();
+
                         // We try to load the layout params set in the <include /> tag. If
                         // they don't exist, we will rely on the layout params set in the
                         // included XML file.
@@ -934,27 +943,21 @@
                         // successfully loaded layout params from the <include /> tag,
                         // false means we need to rely on the included layout params.
                         ViewGroup.LayoutParams params = null;
-                        try {
-                            params = group.generateLayoutParams(attrs);
-                        } catch (RuntimeException e) {
-                            params = group.generateLayoutParams(childAttrs);
-                        } finally {
-                            if (params != null) {
-                                view.setLayoutParams(params);
+                        if (hasWidth && hasHeight) {
+                            try {
+                                params = group.generateLayoutParams(attrs);
+                            } catch (RuntimeException e) {
+                                // Ignore, just fail over to child attrs.
                             }
                         }
+                        if (params == null) {
+                            params = group.generateLayoutParams(childAttrs);
+                        }
+                        view.setLayoutParams(params);
 
                         // Inflate all children.
                         rInflate(childParser, view, childAttrs, true, true);
 
-                        final TypedArray a = context.obtainStyledAttributes(
-                                attrs, com.android.internal.R.styleable.Include);
-                        final int id = a.getResourceId(
-                                com.android.internal.R.styleable.Include_id, View.NO_ID);
-                        final int visibility = a.getInt(
-                                com.android.internal.R.styleable.Include_visibility, -1);
-                        a.recycle();
-
                         if (id != View.NO_ID) {
                             view.setId(id);
                         }
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 1c5c41c..5e45c8f 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -402,6 +402,23 @@
     public static final int FLAG_TAINTED = 0x80000000;
 
     /**
+     * Private flag indicating that this event was synthesized by the system and
+     * should be delivered to the accessibility focused view first. When being
+     * dispatched such an event is not handled by predecessors of the accessibility
+     * focused view and after the event reaches that view the flag is cleared and
+     * normal event dispatch is performed. This ensures that the platform can click
+     * on any view that has accessibility focus which is semantically equivalent to
+     * asking the view to perform a click accessibility action but more generic as
+     * views not implementing click action correctly can still be activated.
+     *
+     * @hide
+     * @see #isTargetAccessibilityFocus()
+     * @see #setTargetAccessibilityFocus(boolean)
+     */
+    public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
+
+
+    /**
      * Flag indicating the motion event intersected the top edge of the screen.
      */
     public static final int EDGE_TOP = 0x00000001;
@@ -1766,6 +1783,20 @@
         nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED);
     }
 
+    /** @hide */
+    public final boolean isTargetAccessibilityFocus() {
+        final int flags = getFlags();
+        return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0;
+    }
+
+    /** @hide */
+    public final void setTargetAccessibilityFocus(boolean targetsFocus) {
+        final int flags = getFlags();
+        nativeSetFlags(mNativePtr, targetsFocus
+                ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS
+                : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
+    }
+
     /**
      * Returns the time (in ms) when the user originally pressed down to start
      * a stream of position events.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1e6f6c9..f60e2f8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2544,6 +2544,20 @@
     public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
 
     /**
+     * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that
+     * is compatible with light status bar backgrounds.
+     *
+     * <p>For this to take effect, the window must request
+     * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+     *         FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not
+     * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS
+     *         FLAG_TRANSLUCENT_STATUS}.
+     *
+     * @see android.R.attr#windowHasLightStatusBar
+     */
+    public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
+
+    /**
      * @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
      */
     public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;
@@ -4569,11 +4583,18 @@
     }
 
     /**
-     * Register a callback to be invoked when the scroll position of this view
-     * changed.
+     * Register a callback to be invoked when the scroll X or Y positions of
+     * this view change.
+     * <p>
+     * <b>Note:</b> Some views handle scrolling independently from View and may
+     * have their own separate listeners for scroll-type events. For example,
+     * {@link android.widget.ListView ListView} allows clients to register an
+     * {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener}
+     * to listen for changes in list scroll position.
      *
-     * @param l The callback that will run.
-     * @hide Only used internally.
+     * @param l The listener to notify when the scroll X or Y position changes.
+     * @see android.view.View#getScrollX()
+     * @see android.view.View#getScrollY()
      */
     public void setOnScrollChangeListener(OnScrollChangeListener l) {
         getListenerInfo().mOnScrollChangeListener = l;
@@ -4669,7 +4690,7 @@
      *
      * @see #setClickable(boolean)
      */
-    public void setOnClickListener(OnClickListener l) {
+    public void setOnClickListener(@Nullable OnClickListener l) {
         if (!isClickable()) {
             setClickable(true);
         }
@@ -4693,7 +4714,7 @@
      *
      * @see #setLongClickable(boolean)
      */
-    public void setOnLongClickListener(OnLongClickListener l) {
+    public void setOnLongClickListener(@Nullable OnLongClickListener l) {
         if (!isLongClickable()) {
             setLongClickable(true);
         }
@@ -5409,7 +5430,7 @@
      */
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         event.setSource(this);
-        event.setClassName(View.class.getName());
+        event.setClassName(getAccessibilityClassName());
         event.setPackageName(getContext().getPackageName());
         event.setEnabled(isEnabled());
         event.setContentDescription(mContentDescription);
@@ -5515,12 +5536,23 @@
     }
 
     /**
-     * Gets the location of this view in screen coordintates.
+     * Gets the location of this view in screen coordinates.
      *
      * @param outRect The output location
      * @hide
      */
     public void getBoundsOnScreen(Rect outRect) {
+        getBoundsOnScreen(outRect, false);
+    }
+
+    /**
+     * Gets the location of this view in screen coordinates.
+     *
+     * @param outRect The output location
+     * @param clipToParent Whether to clip child bounds to the parent ones.
+     * @hide
+     */
+    public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
         if (mAttachInfo == null) {
             return;
         }
@@ -5540,6 +5572,13 @@
 
             position.offset(-parentView.mScrollX, -parentView.mScrollY);
 
+            if (clipToParent) {
+                position.left = Math.max(position.left, 0);
+                position.top = Math.max(position.top, 0);
+                position.right = Math.min(position.right, parentView.getWidth());
+                position.bottom = Math.min(position.bottom, parentView.getHeight());
+            }
+
             if (!parentView.hasIdentityMatrix()) {
                 parentView.getMatrix().mapRect(position);
             }
@@ -5561,6 +5600,26 @@
     }
 
     /**
+     * Return the class name of this object to be used for accessibility purposes.
+     * Subclasses should only override this if they are implementing something that
+     * should be seen as a completely new class of view when used by accessibility,
+     * unrelated to the class it is deriving from.  This is used to fill in
+     * {@link AccessibilityNodeInfo#setClassName AccessibilityNodeInfo.setClassName}.
+     */
+    public CharSequence getAccessibilityClassName() {
+        return View.class.getName();
+    }
+
+    /**
+     * Called when assist data is being retrieved from a view as part of
+     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
+     * @param data
+     * @param extras
+     */
+    public void onProvideAssistData(ViewAssistData data, Bundle extras) {
+    }
+
+    /**
      * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
      *
      * Note: Called from the default {@link AccessibilityDelegate}.
@@ -5573,7 +5632,7 @@
         getDrawingRect(bounds);
         info.setBoundsInParent(bounds);
 
-        getBoundsOnScreen(bounds);
+        getBoundsOnScreen(bounds, true);
         info.setBoundsInScreen(bounds);
 
         ViewParent parent = getParentForAccessibility();
@@ -5642,7 +5701,7 @@
         info.setVisibleToUser(isVisibleToUser());
 
         info.setPackageName(mContext.getPackageName());
-        info.setClassName(View.class.getName());
+        info.setClassName(getAccessibilityClassName());
         info.setContentDescription(getContentDescription());
 
         info.setEnabled(isEnabled());
@@ -5769,159 +5828,6 @@
     }
 
     /**
-     * Computes a point on which a sequence of a down/up event can be sent to
-     * trigger clicking this view. This method is for the exclusive use by the
-     * accessibility layer to determine where to send a click event in explore
-     * by touch mode.
-     *
-     * @param interactiveRegion The interactive portion of this window.
-     * @param outPoint The point to populate.
-     * @return True of such a point exists.
-     */
-    boolean computeClickPointInScreenForAccessibility(Region interactiveRegion,
-            Point outPoint) {
-        // Since the interactive portion of the view is a region but as a view
-        // may have a transformation matrix which cannot be applied to a
-        // region we compute the view bounds rectangle and all interactive
-        // predecessor's and sibling's (siblings of predecessors included)
-        // rectangles that intersect the view bounds. At the
-        // end if the view was partially covered by another interactive
-        // view we compute the view's interactive region and pick a point
-        // on its boundary path as regions do not offer APIs to get inner
-        // points. Note that the the code is optimized to fail early and
-        // avoid unnecessary allocations plus computations.
-
-        // The current approach has edge cases that may produce false
-        // positives or false negatives. For example, a portion of the
-        // view may be covered by an interactive descendant of a
-        // predecessor, which we do not compute. Also a view may be handling
-        // raw touch events instead registering click listeners, which
-        // we cannot compute. Despite these limitations this approach will
-        // work most of the time and it is a huge improvement over just
-        // blindly sending the down and up events in the center of the
-        // view.
-
-        // Cannot click on an unattached view.
-        if (mAttachInfo == null) {
-            return false;
-        }
-
-        // Attached to an invisible window means this view is not visible.
-        if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
-            return false;
-        }
-
-        RectF bounds = mAttachInfo.mTmpTransformRect;
-        bounds.set(0, 0, getWidth(), getHeight());
-        List<RectF> intersections = mAttachInfo.mTmpRectList;
-        intersections.clear();
-
-        if (mParent instanceof ViewGroup) {
-            ViewGroup parentGroup = (ViewGroup) mParent;
-            if (!parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
-                    this, bounds, intersections)) {
-                intersections.clear();
-                return false;
-            }
-        }
-
-        // Take into account the window location.
-        final int dx = mAttachInfo.mWindowLeft;
-        final int dy = mAttachInfo.mWindowTop;
-        bounds.offset(dx, dy);
-        offsetRects(intersections, dx, dy);
-
-        if (intersections.isEmpty() && interactiveRegion == null) {
-            outPoint.set((int) bounds.centerX(), (int) bounds.centerY());
-        } else {
-            // This view is partially covered by other views, then compute
-            // the not covered region and pick a point on its boundary.
-            Region region = new Region();
-            region.set((int) bounds.left, (int) bounds.top,
-                    (int) bounds.right, (int) bounds.bottom);
-
-            final int intersectionCount = intersections.size();
-            for (int i = intersectionCount - 1; i >= 0; i--) {
-                RectF intersection = intersections.remove(i);
-                region.op((int) intersection.left, (int) intersection.top,
-                        (int) intersection.right, (int) intersection.bottom,
-                        Region.Op.DIFFERENCE);
-            }
-
-            // If the view is completely covered, done.
-            if (region.isEmpty()) {
-                return false;
-            }
-
-            // Take into account the interactive portion of the window
-            // as the rest is covered by other windows. If no such a region
-            // then the whole window is interactive.
-            if (interactiveRegion != null) {
-                region.op(interactiveRegion, Region.Op.INTERSECT);
-            }
-
-            // Take into account the window bounds.
-            final View root = getRootView();
-            if (root != null) {
-                region.op(dx, dy, root.getWidth() + dx, root.getHeight() + dy, Region.Op.INTERSECT);
-            }
-
-            // If the view is completely covered, done.
-            if (region.isEmpty()) {
-                return false;
-            }
-
-            // Try a shortcut here.
-            if (region.isRect()) {
-                Rect regionBounds = mAttachInfo.mTmpInvalRect;
-                region.getBounds(regionBounds);
-                outPoint.set(regionBounds.centerX(), regionBounds.centerY());
-                return true;
-            }
-
-            // Get the a point on the region boundary path.
-            Path path = region.getBoundaryPath();
-            PathMeasure pathMeasure = new PathMeasure(path, false);
-            final float[] coordinates = mAttachInfo.mTmpTransformLocation;
-
-            // Without loss of generality pick a point.
-            final float point = pathMeasure.getLength() * 0.01f;
-            if (!pathMeasure.getPosTan(point, coordinates, null)) {
-                return false;
-            }
-
-            outPoint.set(Math.round(coordinates[0]), Math.round(coordinates[1]));
-        }
-
-        return true;
-    }
-
-    /**
-     * Adds the clickable rectangles withing the bounds of this view. They
-     * may overlap. This method is intended for use only by the accessibility
-     * layer.
-     *
-     * @param outRects List to which to add clickable areas.
-     *
-     * @hide
-     */
-    public void addClickableRectsForAccessibility(List<RectF> outRects) {
-        if (isClickable() || isLongClickable()) {
-            RectF bounds = new RectF();
-            bounds.set(0, 0, getWidth(), getHeight());
-            outRects.add(bounds);
-        }
-    }
-
-    static void offsetRects(List<RectF> rects, float offsetX, float offsetY) {
-        final int rectCount = rects.size();
-        for (int i = 0; i < rectCount; i++) {
-            RectF intersection = rects.get(i);
-            intersection.offset(offsetX, offsetY);
-        }
-    }
-
-    /**
      * Returns the delegate for implementing accessibility support via
      * composition. For more details see {@link AccessibilityDelegate}.
      *
@@ -6790,7 +6696,6 @@
     @RemotableViewMethod
     public void setVisibility(@Visibility int visibility) {
         setFlags(visibility, VISIBILITY_MASK);
-        if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
     }
 
     /**
@@ -8536,6 +8441,16 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     public boolean dispatchTouchEvent(MotionEvent event) {
+        // If the event should be handled by accessibility focus first.
+        if (event.isTargetAccessibilityFocus()) {
+            // We don't have focus or no virtual descendant has it, do not handle the event.
+            if (!isAccessibilityFocusedViewOrHost()) {
+                return false;
+            }
+            // We have focus and got the event, then use normal event dispatch.
+            event.setTargetAccessibilityFocus(false);
+        }
+
         boolean result = false;
 
         if (mInputEventConsistencyVerifier != null) {
@@ -8578,6 +8493,11 @@
         return result;
     }
 
+    boolean isAccessibilityFocusedViewOrHost() {
+        return isAccessibilityFocused() || (getViewRootImpl() != null && getViewRootImpl()
+                .getAccessibilityFocusedHost() == this);
+    }
+
     /**
      * Filter the touch event to apply security policies.
      *
@@ -8816,20 +8736,28 @@
     }
 
     /**
-     * Called when the visibility of the view or an ancestor of the view is changed.
-     * @param changedView The view whose visibility changed. Could be 'this' or
-     * an ancestor view.
-     * @param visibility The new visibility of changedView: {@link #VISIBLE},
-     * {@link #INVISIBLE} or {@link #GONE}.
+     * Called when the visibility of the view or an ancestor of the view has
+     * changed.
+     *
+     * @param changedView The view whose visibility changed. May be
+     *                    {@code this} or an ancestor view.
+     * @param visibility The new visibility, one of {@link #VISIBLE},
+     *                   {@link #INVISIBLE} or {@link #GONE}.
      */
     protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
-        if (visibility == VISIBLE) {
+        final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
+        if (visible) {
             if (mAttachInfo != null) {
                 initialAwakenScrollBars();
             } else {
                 mPrivateFlags |= PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
             }
         }
+
+        final Drawable dr = mBackground;
+        if (dr != null && visible != dr.isVisible()) {
+            dr.setVisible(visible, false);
+        }
     }
 
     /**
@@ -9959,9 +9887,15 @@
 
     /**
      * Interface definition for a callback to be invoked when the scroll
-     * position of a view changes.
+     * X or Y positions of a view change.
+     * <p>
+     * <b>Note:</b> Some views handle scrolling independently from View and may
+     * have their own separate listeners for scroll-type events. For example,
+     * {@link android.widget.ListView ListView} allows clients to register an
+     * {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener}
+     * to listen for changes in list scroll position.
      *
-     * @hide Only used internally.
+     * @see #setOnScrollChangeListener(View.OnScrollChangeListener)
      */
     public interface OnScrollChangeListener {
         /**
@@ -12962,7 +12896,7 @@
                         Interpolator.Result.FREEZE_END) {
                     cache.state = ScrollabilityCache.OFF;
                 } else {
-                    cache.scrollBar.setAlpha(Math.round(values[0]));
+                    cache.scrollBar.mutate().setAlpha(Math.round(values[0]));
                 }
 
                 // This will make the scroll bars inval themselves after
@@ -12972,7 +12906,7 @@
             } else {
                 // We're just on -- but we may have been fading before so
                 // reset alpha
-                cache.scrollBar.setAlpha(255);
+                cache.scrollBar.mutate().setAlpha(255);
             }
 
 
@@ -14889,10 +14823,9 @@
     void setDisplayListProperties(RenderNode renderNode) {
         if (renderNode != null) {
             renderNode.setHasOverlappingRendering(hasOverlappingRendering());
-            if (mParent instanceof ViewGroup) {
-                renderNode.setClipToBounds(
-                        (((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
-            }
+            renderNode.setClipToBounds(mParent instanceof ViewGroup
+                    && ((ViewGroup) mParent).getClipChildren());
+
             float alpha = 1;
             if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
                     ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
@@ -16642,6 +16575,7 @@
 
         if (changed) {
             requestLayout();
+            invalidateOutline();
         }
     }
 
diff --git a/core/java/android/view/ViewAssistData.java b/core/java/android/view/ViewAssistData.java
new file mode 100644
index 0000000..74436ea
--- /dev/null
+++ b/core/java/android/view/ViewAssistData.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * Container for storing data generated by {@link View#onProvideAssistData
+ * View.onProvideAssistData}.
+ */
+public abstract class ViewAssistData {
+    public abstract void setText(CharSequence text);
+    public abstract void setText(CharSequence text, int selectionStart, int selectionEnd);
+    public abstract void setHint(CharSequence hint);
+
+    public abstract CharSequence getText();
+    public abstract int getTextSelectionStart();
+    public abstract int getTextSelectionEnd();
+    public abstract CharSequence getHint();
+}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ee9845f..0e114ef 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -474,9 +474,6 @@
     @ViewDebug.ExportedProperty(category = "layout")
     private int mChildCountWithTransientState = 0;
 
-    // Iterator over the children in decreasing Z order (top children first).
-    private OrderedChildIterator mOrderedChildIterator;
-
     /**
      * Currently registered axes for nested scrolling. Flag set consisting of
      * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
@@ -782,208 +779,6 @@
     }
 
     /**
-     * Translates the given bounds and intersections from child coordinates to
-     * local coordinates. In case any interactive sibling of the calling child
-     * covers the latter, a new intersections is added to the intersection list.
-     * This method is for the exclusive use by the accessibility layer to compute
-     * a point where a sequence of down and up events would click on a view.
-     *
-     * @param child The child making the call.
-     * @param bounds The bounds to translate in child coordinates.
-     * @param intersections The intersections of interactive views covering the child.
-     * @return True if the bounds and intersections were computed, false otherwise.
-     */
-    boolean translateBoundsAndIntersectionsInWindowCoordinates(View child,
-            RectF bounds, List<RectF> intersections) {
-        // Not attached, done.
-        if (mAttachInfo == null) {
-            return false;
-        }
-
-        if (getAlpha() <= 0 || getTransitionAlpha() <= 0 ||
-                getVisibility() != VISIBLE) {
-            // Cannot click on a view with an invisible predecessor.
-            return false;
-        }
-
-        // Compensate for the child transformation.
-        if (!child.hasIdentityMatrix()) {
-            Matrix matrix = child.getMatrix();
-            matrix.mapRect(bounds);
-            final int intersectionCount = intersections.size();
-            for (int i = 0; i < intersectionCount; i++) {
-                RectF intersection = intersections.get(i);
-                matrix.mapRect(intersection);
-            }
-        }
-
-        // Translate the bounds from child to parent coordinates.
-        final int dx = child.mLeft - mScrollX;
-        final int dy = child.mTop - mScrollY;
-        bounds.offset(dx, dy);
-        offsetRects(intersections, dx, dy);
-
-        // If the bounds do not intersect our bounds, done.
-        if (!bounds.intersects(0, 0, getWidth(), getHeight())) {
-            return false;
-        }
-
-        // Clip the bounds by our bounds.
-        bounds.left = Math.max(bounds.left, 0);
-        bounds.top = Math.max(bounds.top, 0);
-        bounds.right = Math.min(bounds.right, getWidth());
-        bounds.bottom = Math.min(bounds.bottom, getHeight());
-
-        Iterator<View> iterator = obtainOrderedChildIterator();
-        while (iterator.hasNext()) {
-            View sibling = iterator.next();
-
-            // We care only about siblings over the child.
-            if (sibling == child) {
-                break;
-            }
-
-            // Ignore invisible views as they are not interactive.
-            if (!isVisible(sibling)) {
-                continue;
-            }
-
-            // Compute the sibling bounds in its coordinates.
-            RectF siblingBounds = mAttachInfo.mTmpTransformRect1;
-            siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight());
-
-            // Translate the sibling bounds to our coordinates.
-            offsetChildRectToMyCoords(siblingBounds, sibling);
-
-            // Compute the intersection between the child and the sibling.
-            if (siblingBounds.intersect(bounds)) {
-                List<RectF> clickableRects = new ArrayList<>();
-                sibling.addClickableRectsForAccessibility(clickableRects);
-
-                final int clickableRectCount = clickableRects.size();
-                for (int j = 0; j < clickableRectCount; j++) {
-                    RectF clickableRect = clickableRects.get(j);
-
-                    // Translate the clickable rect to our coordinates.
-                    offsetChildRectToMyCoords(clickableRect, sibling);
-
-                    // Compute the intersection between the child and the clickable rects.
-                    if (clickableRect.intersect(bounds)) {
-                        // If a clickable rect completely covers the child, done.
-                        if (clickableRect.equals(bounds)) {
-                            releaseOrderedChildIterator();
-                            return false;
-                        }
-                        // Keep track of the intersection rectangle.
-                        intersections.add(clickableRect);
-                    }
-                }
-            }
-        }
-
-        releaseOrderedChildIterator();
-
-        if (mParent instanceof ViewGroup) {
-            ViewGroup parentGroup = (ViewGroup) mParent;
-            return parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
-                    this, bounds, intersections);
-        }
-
-        return true;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public void addClickableRectsForAccessibility(List<RectF> outRects) {
-        int sizeBefore = outRects.size();
-
-        super.addClickableRectsForAccessibility(outRects);
-
-        // If we added ourselves, then no need to visit children.
-        if (outRects.size() > sizeBefore) {
-            return;
-        }
-
-        Iterator<View> iterator = obtainOrderedChildIterator();
-        while (iterator.hasNext()) {
-            View child = iterator.next();
-
-            // Cannot click on an invisible view.
-            if (!isVisible(child)) {
-                continue;
-            }
-
-            sizeBefore = outRects.size();
-
-            // Add clickable rects in the child bounds.
-            child.addClickableRectsForAccessibility(outRects);
-
-            // Offset the clickable rects for out children to our coordinates.
-            final int sizeAfter = outRects.size();
-            for (int j = sizeBefore; j < sizeAfter; j++) {
-                RectF rect = outRects.get(j);
-
-                // Translate the clickable rect to our coordinates.
-                offsetChildRectToMyCoords(rect, child);
-
-                // If a clickable rect fills the parent, done.
-                if ((int) rect.left == 0 && (int) rect.top == 0
-                        && (int) rect.right == mRight && (int) rect.bottom == mBottom) {
-                    releaseOrderedChildIterator();
-                    return;
-                }
-            }
-        }
-
-        releaseOrderedChildIterator();
-    }
-
-    private void offsetChildRectToMyCoords(RectF rect, View child) {
-        if (!child.hasIdentityMatrix()) {
-            child.getMatrix().mapRect(rect);
-        }
-        final int childDx = child.mLeft - mScrollX;
-        final int childDy = child.mTop - mScrollY;
-        rect.offset(childDx, childDy);
-    }
-
-    private static boolean isVisible(View view) {
-        return (view.getAlpha() > 0 && view.getTransitionAlpha() > 0 &&
-                view.getVisibility() == VISIBLE);
-    }
-
-    /**
-     * Obtains the iterator to traverse the children in a descending Z order.
-     * Only one party can use the iterator at any given time and you cannot
-     * modify the children while using this iterator. Acquisition if already
-     * obtained is an error.
-     *
-     * @return The child iterator.
-     */
-    OrderedChildIterator obtainOrderedChildIterator() {
-        if (mOrderedChildIterator == null) {
-            mOrderedChildIterator = new OrderedChildIterator();
-        } else if (mOrderedChildIterator.isInitialized()) {
-            throw new IllegalStateException("Already obtained");
-        }
-        mOrderedChildIterator.initialize();
-        return mOrderedChildIterator;
-    }
-
-    /**
-     * Releases the iterator to traverse the children in a descending Z order.
-     * Release if not obtained is an error.
-     */
-    void releaseOrderedChildIterator() {
-        if (mOrderedChildIterator == null || !mOrderedChildIterator.isInitialized()) {
-            throw new IllegalStateException("Not obtained");
-        }
-        mOrderedChildIterator.release();
-    }
-
-    /**
      * Called when a child view has changed whether or not it is tracking transient state.
      */
     public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
@@ -2138,6 +1933,15 @@
             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
         }
 
+        // Whether this event should be handled by the accessibility focus first.
+        final boolean targetAccessibilityFocus = ev.isTargetAccessibilityFocus();
+
+        // If the event targets the accessibility focused view and this is it, start
+        // normal event dispatch. Maybe a descendant is what will handle the click.
+        if (targetAccessibilityFocus && isAccessibilityFocusedViewOrHost()) {
+            ev.setTargetAccessibilityFocus(false);
+        }
+
         boolean handled = false;
         if (onFilterTouchEventForSecurity(ev)) {
             final int action = ev.getAction();
@@ -2154,19 +1958,24 @@
 
             // Check for interception.
             final boolean intercepted;
-            if (actionMasked == MotionEvent.ACTION_DOWN
-                    || mFirstTouchTarget != null) {
-                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
-                if (!disallowIntercept) {
-                    intercepted = onInterceptTouchEvent(ev);
-                    ev.setAction(action); // restore action in case it was changed
+            if (!targetAccessibilityFocus) {
+                if (actionMasked == MotionEvent.ACTION_DOWN
+                        || mFirstTouchTarget != null) {
+                    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+                    if (!disallowIntercept) {
+                        intercepted = onInterceptTouchEvent(ev);
+                        ev.setAction(action); // restore action in case it was changed
+                    } else {
+                        intercepted = false;
+                    }
                 } else {
-                    intercepted = false;
+                    // There are no touch targets and this action is not an initial down
+                    // so this view group continues to intercept touches.
+                    intercepted = true;
                 }
             } else {
-                // There are no touch targets and this action is not an initial down
-                // so this view group continues to intercept touches.
-                intercepted = true;
+                // If event should reach the accessibility focus first, do not intercept it.
+                intercepted = false;
             }
 
             // Check for cancelation.
@@ -2180,7 +1989,8 @@
             if (!canceled && !intercepted) {
                 if (actionMasked == MotionEvent.ACTION_DOWN
                         || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
-                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
+                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE
+                        || targetAccessibilityFocus) {
                     final int actionIndex = ev.getActionIndex(); // always 0 for down
                     final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                             : TouchTarget.ALL_POINTER_IDS;
@@ -2881,6 +2691,9 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
+        if (getAccessibilityNodeProvider() != null) {
+            return;
+        }
         if (mAttachInfo != null) {
             final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
             childrenForAccessibility.clear();
@@ -7472,57 +7285,4 @@
 
         canvas.drawLines(sDebugLines, paint);
     }
-
-    private final class OrderedChildIterator implements Iterator<View> {
-        private List<View> mOrderedChildList;
-        private boolean mUseCustomOrder;
-        private int mCurrentIndex;
-        private boolean mInitialized;
-
-        public void initialize() {
-            mOrderedChildList = buildOrderedChildList();
-            mUseCustomOrder = (mOrderedChildList == null)
-                    && isChildrenDrawingOrderEnabled();
-            mCurrentIndex = mChildrenCount - 1;
-            mInitialized = true;
-        }
-
-        public void release() {
-            if (mOrderedChildList != null) {
-                mOrderedChildList.clear();
-            }
-            mUseCustomOrder = false;
-            mCurrentIndex = 0;
-            mInitialized = false;
-        }
-
-        public boolean isInitialized() {
-            return mInitialized;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return (mCurrentIndex >= 0);
-        }
-
-        @Override
-        public View next() {
-            if (!hasNext()) {
-                throw new NoSuchElementException("No such element");
-            }
-            return getChild(mCurrentIndex--);
-        }
-
-        private View getChild(int index) {
-            final int childIndex = mUseCustomOrder
-                    ? getChildDrawingOrder(mChildrenCount, index) : index;
-            return (mOrderedChildList == null)
-                    ? mChildren[childIndex] : mOrderedChildList.get(childIndex);
-        }
-
-        @Override
-        public void remove() {
-            throw new UnsupportedOperationException();
-        }
-    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 87d9a58..570cb72 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -472,8 +472,10 @@
 
                 // Compute surface insets required to draw at specified Z value.
                 // TODO: Use real shadow insets for a constant max Z.
-                final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
-                attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+                if (!attrs.hasManualSurfaceInsets) {
+                    final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+                    attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+                }
 
                 CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
                 mTranslator = compatibilityInfo.getTranslator();
@@ -649,6 +651,10 @@
         return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
     }
 
+    public CharSequence getTitle() {
+        return mWindowAttributes.getTitle();
+    }
+
     void destroyHardwareResources() {
         if (mAttachInfo.mHardwareRenderer != null) {
             mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
@@ -760,6 +766,7 @@
             final int oldInsetRight = mWindowAttributes.surfaceInsets.right;
             final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom;
             final int oldSoftInputMode = mWindowAttributes.softInputMode;
+            final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets;
 
             // Keep track of the actual window flags supplied by the client.
             mClientWindowLayoutFlags = attrs.flags;
@@ -786,6 +793,7 @@
             // Restore old surface insets.
             mWindowAttributes.surfaceInsets.set(
                     oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
+            mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
 
             applyKeepScreenOnFlag(mWindowAttributes);
 
@@ -1326,6 +1334,11 @@
             }
         }
 
+        // Non-visible windows can't hold accessibility focus.
+        if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
+            host.clearAccessibilityFocus();
+        }
+
         // Execute enqueued actions on every traversal in case a detached view enqueued an action
         getRunQueue().executeActions(mAttachInfo.mHandler);
 
@@ -2703,7 +2716,7 @@
 
         final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
         if (provider == null) {
-            host.getBoundsOnScreen(bounds);
+            host.getBoundsOnScreen(bounds, true);
         } else if (mAccessibilityFocusedVirtualView != null) {
             mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
         } else {
@@ -6835,26 +6848,6 @@
         }
 
         @Override
-        public void computeClickPointInScreen(long accessibilityNodeId, Region interactiveRegion,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
-            ViewRootImpl viewRootImpl = mViewRootImpl.get();
-            if (viewRootImpl != null && viewRootImpl.mView != null) {
-                viewRootImpl.getAccessibilityInteractionController()
-                        .computeClickPointInScreenClientThread(accessibilityNodeId,
-                                interactiveRegion, interactionId, callback, interrogatingPid,
-                                interrogatingTid, spec);
-            } else {
-                // We cannot make the call and notify the caller so it does not wait.
-                try {
-                    callback.setComputeClickPointInScreenActionResult(null, interactionId);
-                } catch (RemoteException re) {
-                    /* best effort - ignore */
-                }
-            }
-        }
-
-        @Override
         public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
                 String viewId, Region interactiveRegion, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 3f2f3a5..8704356 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -102,10 +102,12 @@
      */
     public static final int FEATURE_SWIPE_TO_DISMISS = 11;
     /**
-     * Flag for requesting that window content changes should be represented
-     * with scenes and transitions.
+     * Flag for requesting that window content changes should be animated using a
+     * TransitionManager.
      *
-     * TODO Add docs
+     * <p>The TransitionManager is set using
+     * {@link #setTransitionManager(android.transition.TransitionManager)}. If none is set,
+     * a default TransitionManager will be used.</p>
      *
      * @see #setContentView
      */
@@ -1019,10 +1021,16 @@
      * <p>Note that calling this function "locks in" various characteristics
      * of the window that can not, from this point forward, be changed: the
      * features that have been requested with {@link #requestFeature(int)},
-     * and certain window flags as described in {@link #setFlags(int, int)}.
+     * and certain window flags as described in {@link #setFlags(int, int)}.</p>
+     *
+     * <p>If {@link #FEATURE_CONTENT_TRANSITIONS} is set, the window's
+     * TransitionManager will be used to animate content from the current
+     * content View to view.</p>
      *
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
+     * @see #getTransitionManager()
+     * @see #setTransitionManager(android.transition.TransitionManager)
      */
     public abstract void setContentView(View view, ViewGroup.LayoutParams params);
 
@@ -1467,6 +1475,7 @@
      * {@link #setContentView}) if {@link #FEATURE_CONTENT_TRANSITIONS} has been granted.</p>
      *
      * @return This window's content TransitionManager or null if none is set.
+     * @attr ref android.R.styleable#Window_windowContentTransitionManager
      */
     public TransitionManager getTransitionManager() {
         return null;
@@ -1477,6 +1486,7 @@
      * Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
      *
      * @param tm The TransitionManager to use for scene changes.
+     * @attr ref android.R.styleable#Window_windowContentTransitionManager
      */
     public void setTransitionManager(TransitionManager tm) {
         throw new UnsupportedOperationException();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 094a8a1..905d6d7 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.SystemApi;
 import android.app.Presentation;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -501,13 +502,6 @@
         public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
 
         /**
-         * Window type: Behind the universe of the real windows.
-         * In multiuser systems shows on all users' windows.
-         * @hide
-         */
-        public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25;
-
-        /**
          * Window type: Display overlay window.  Used to simulate secondary display devices.
          * In multiuser systems shows on all users' windows.
          * @hide
@@ -1332,6 +1326,16 @@
          * @hide
          */
         public final Rect surfaceInsets = new Rect();
+
+        /**
+         * Whether the surface insets have been manually set. When set to
+         * {@code false}, the view root will automatically determine the
+         * appropriate surface insets.
+         *
+         * @see #surfaceInsets
+         * @hide
+         */
+        public boolean hasManualSurfaceInsets;
     
         /**
          * The desired bitmap format.  May be one of the constants in
@@ -1590,7 +1594,19 @@
         public final CharSequence getTitle() {
             return mTitle;
         }
-    
+
+        /** @hide */
+        @SystemApi
+        public final void setUserActivityTimeout(long timeout) {
+            userActivityTimeout = timeout;
+        }
+
+        /** @hide */
+        @SystemApi
+        public final long getUserActivityTimeout() {
+            return userActivityTimeout;
+        }
+
         public int describeContents() {
             return 0;
         }
@@ -1628,6 +1644,7 @@
             out.writeInt(surfaceInsets.top);
             out.writeInt(surfaceInsets.right);
             out.writeInt(surfaceInsets.bottom);
+            out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
         }
 
@@ -1676,6 +1693,7 @@
             surfaceInsets.top = in.readInt();
             surfaceInsets.right = in.readInt();
             surfaceInsets.bottom = in.readInt();
+            hasManualSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
         }
 
@@ -1858,6 +1876,11 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
+            if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
+                hasManualSurfaceInsets = o.hasManualSurfaceInsets;
+                changes |= SURFACE_INSETS_CHANGED;
+            }
+
             if (needsMenuKey != o.needsMenuKey) {
                 needsMenuKey = o.needsMenuKey;
                 changes |= NEEDS_MENU_KEY_CHANGED;
@@ -1966,8 +1989,11 @@
             if (userActivityTimeout >= 0) {
                 sb.append(" userActivityTimeout=").append(userActivityTimeout);
             }
-            if (!surfaceInsets.equals(Insets.NONE)) {
+            if (!surfaceInsets.equals(Insets.NONE) || hasManualSurfaceInsets) {
                 sb.append(" surfaceInsets=").append(surfaceInsets);
+                if (hasManualSurfaceInsets) {
+                    sb.append(" (manual)");
+                }
             }
             if (needsMenuKey != NEEDS_MENU_UNSET) {
                 sb.append(" needsMenuKey=");
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index ed17e3f..279627a 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -190,6 +190,39 @@
         }
     }
 
+    public ArrayList<ViewRootImpl> getRootViews(IBinder token) {
+        ArrayList<ViewRootImpl> views = new ArrayList<>();
+        synchronized (mLock) {
+            final int numRoots = mRoots.size();
+            for (int i = 0; i < numRoots; ++i) {
+                WindowManager.LayoutParams params = mParams.get(i);
+                if (params.token == null) {
+                    continue;
+                }
+                if (params.token != token) {
+                    boolean isChild = false;
+                    if (params.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
+                            && params.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                        for (int j = 0 ; j < numRoots; ++j) {
+                            View viewj = mViews.get(j);
+                            WindowManager.LayoutParams paramsj = mParams.get(j);
+                            if (params.token == viewj.getWindowToken()
+                                    && paramsj.token == token) {
+                                isChild = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (!isChild) {
+                        continue;
+                    }
+                }
+                views.add(mRoots.get(i));
+            }
+        }
+        return views;
+    }
+
     public View getRootView(String name) {
         synchronized (mLock) {
             for (int i = mRoots.size() - 1; i >= 0; --i) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b8e94ee..3f35612 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
@@ -105,6 +106,13 @@
     public final static String EXTRA_HDMI_PLUGGED_STATE = "state";
 
     /**
+     * Set to {@code true} when intent was invoked from pressing the home key.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FROM_HOME_KEY = "android.intent.extra.FROM_HOME_KEY";
+
+    /**
      * Pass this event to the user / app.  To be returned from
      * {@link #interceptKeyBeforeQueueing}.
      */
@@ -579,13 +587,6 @@
     public int getMaxWallpaperLayer();
 
     /**
-     * Return the window layer at which windows appear above the normal
-     * universe (that is no longer impacted by the universe background
-     * transform).
-     */
-    public int getAboveUniverseLayer();
-
-    /**
      * Return the display width available after excluding any screen
      * decorations that can never be removed.  That is, system bar or
      * button bar.
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 374f7e0..cefd34d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -99,8 +99,6 @@
 
     private boolean mPerformAccessibilityActionResult;
 
-    private Point mComputeClickPointResult;
-
     private Message mSameThreadMessage;
 
     private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
@@ -522,43 +520,6 @@
         return false;
     }
 
-    /**
-     * Computes a point in screen coordinates where sending a down/up events would
-     * perform a click on an {@link AccessibilityNodeInfo}.
-     *
-     * @param connectionId The id of a connection for interacting with the system.
-     * @param accessibilityWindowId A unique window id. Use
-     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
-     *     to query the currently active window.
-     * @param accessibilityNodeId A unique view id or virtual descendant id from
-     *     where to start the search. Use
-     *     {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
-     *     to start from the root.
-     * @return Point the click point of null if no such point.
-     */
-    public Point computeClickPointInScreen(int connectionId, int accessibilityWindowId,
-            long accessibilityNodeId) {
-        try {
-            IAccessibilityServiceConnection connection = getConnection(connectionId);
-            if (connection != null) {
-                final int interactionId = mInteractionIdCounter.getAndIncrement();
-                final boolean success = connection.computeClickPointInScreen(
-                        accessibilityWindowId, accessibilityNodeId,
-                        interactionId, this, Thread.currentThread().getId());
-                if (success) {
-                    return getComputeClickPointInScreenResultAndClear(interactionId);
-                }
-            } else {
-                if (DEBUG) {
-                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
-                }
-            }
-        } catch (RemoteException re) {
-            Log.w(LOG_TAG, "Error while calling remote computeClickPointInScreen", re);
-        }
-        return null;
-    }
-
     public void clearCache() {
         sAccessibilityCache.clear();
     }
@@ -674,34 +635,6 @@
     }
 
     /**
-     * Gets the result of a request to compute a point in screen for clicking on a node.
-     *
-     * @param interactionId The interaction id to match the result with the request.
-     * @return The point or null if no such point.
-     */
-    private Point getComputeClickPointInScreenResultAndClear(int interactionId) {
-        synchronized (mInstanceLock) {
-            final boolean success = waitForResultTimedLocked(interactionId);
-            Point result = success ? mComputeClickPointResult : null;
-            clearResultLocked();
-            return result;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setComputeClickPointInScreenActionResult(Point point, int interactionId) {
-        synchronized (mInstanceLock) {
-            if (interactionId > mInteractionId) {
-                mComputeClickPointResult = point;
-                mInteractionId = interactionId;
-            }
-            mInstanceLock.notifyAll();
-        }
-    }
-
-    /**
      * Clears the result state.
      */
     private void clearResultLocked() {
@@ -709,7 +642,6 @@
         mFindAccessibilityNodeInfoResult = null;
         mFindAccessibilityNodeInfosResult = null;
         mPerformAccessibilityActionResult = false;
-        mComputeClickPointResult = null;
     }
 
     /**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index b5afdf7..6096d7d 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -721,7 +721,7 @@
      * @return Whether the refresh succeeded.
      */
     public boolean refresh() {
-        return refresh(false);
+        return refresh(true);
     }
 
     /**
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 66a3f46..cecc4af 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -54,8 +54,4 @@
     void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments,
         int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
         int interrogatingPid, long interrogatingTid);
-
-    void computeClickPointInScreen(long accessibilityNodeId, in Region bounds, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
-        long interrogatingTid, in MagnificationSpec spec);
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index f480216..42ae1b3 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -52,12 +52,4 @@
      * @param interactionId The interaction id to match the result with the request.
      */
     void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
-
-    /**
-     * Sets the result of a request to compute a point for clicking in a view.
-     *
-     * @param point The point of null if no such point.
-     * @param interactionId The interaction id to match the result with the request.
-     */
-    void setComputeClickPointInScreenActionResult(in Point point, int interactionId);
 }
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
index 2185658de3..07402b3 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -42,6 +42,8 @@
 
     /**
      * Gets whether a gesture (such as a click) was associated with the request.
+     * For security reasons in certain situations this method may return false even though the
+     * sequence of events which caused the request to be created was initiated by a user gesture.
      *
      * @return whether a gesture was associated with the request.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4a7cc6d..01a506c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2387,11 +2387,14 @@
         return mProvider.getViewDelegate().shouldDelayChildPressedState();
     }
 
+    public CharSequence getAccessibilityClassName() {
+        return WebView.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(WebView.class.getName());
         mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info);
     }
 
@@ -2399,7 +2402,6 @@
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(WebView.class.getName());
         mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
     }
 
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index d52dd60..1cc899d 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -50,7 +50,9 @@
      * is called once for each main frame load so a page with iframes or
      * framesets will call onPageStarted one time for the main frame. This also
      * means that onPageStarted will not be called when the contents of an
-     * embedded frame changes, i.e. clicking a link whose target is an iframe.
+     * embedded frame changes, i.e. clicking a link whose target is an iframe,
+     * it will also not be called for fragment navigations (navigations to
+     * #fragment_id).
      *
      * @param view The WebView that is initiating the callback.
      * @param url The url to be loaded.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 7ea3265..e87a117 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -578,6 +578,12 @@
     private boolean mIsChildViewEnabled;
 
     /**
+     * The cached drawable state for the selector. Accounts for child enabled
+     * state, but otherwise identical to the view's own drawable state.
+     */
+    private int[] mSelectorState;
+
+    /**
      * The last scroll state reported to clients through {@link OnScrollListener}.
      */
     private int mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE;
@@ -1486,18 +1492,15 @@
         super.sendAccessibilityEventInternal(eventType);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(AbsListView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return AbsListView.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(AbsListView.class.getName());
         if (isEnabled()) {
             if (canScrollUp()) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
@@ -2789,7 +2792,7 @@
     void updateSelectorState() {
         if (mSelector != null) {
             if (shouldShowSelector()) {
-                mSelector.setState(getDrawableState());
+                mSelector.setState(getDrawableStateForSelector());
             } else {
                 mSelector.setState(StateSet.NOTHING);
             }
@@ -2802,12 +2805,11 @@
         updateSelectorState();
     }
 
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
+    private int[] getDrawableStateForSelector() {
         // If the child view is enabled then do the default behavior.
         if (mIsChildViewEnabled) {
             // Common case
-            return super.onCreateDrawableState(extraSpace);
+            return super.getDrawableState();
         }
 
         // The selector uses this View's drawable state. The selected child view
@@ -2815,10 +2817,12 @@
         // states.
         final int enabledState = ENABLED_STATE_SET[0];
 
-        // If we don't have any extra space, it will return one of the static state arrays,
-        // and clearing the enabled state on those arrays is a bad thing!  If we specify
-        // we need extra space, it will create+copy into a new array that safely mutable.
-        int[] state = super.onCreateDrawableState(extraSpace + 1);
+        // If we don't have any extra space, it will return one of the static
+        // state arrays, and clearing the enabled state on those arrays is a
+        // bad thing! If we specify we need extra space, it will create+copy
+        // into a new array that is safely mutable.
+        final int[] state = onCreateDrawableState(1);
+
         int enabledPos = -1;
         for (int i = state.length - 1; i >= 0; i--) {
             if (state[i] == enabledState) {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 1f7be63..4f6ea23 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -715,18 +715,15 @@
         return super.onKeyDown(keyCode, event);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(AbsSeekBar.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return AbsSeekBar.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(AbsSeekBar.class.getName());
 
         if (isEnabled()) {
             final int progress = getProgress();
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 5d8d48fd..e432747 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -470,17 +470,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(AbsSpinner.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(AbsSpinner.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return AbsSpinner.class.getName();
     }
 }
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 428b6ce..9b977fa 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -955,11 +955,15 @@
         return false;
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return AdapterView.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(AdapterView.class.getName());
         info.setScrollable(isScrollableForAccessibility());
         View selectedView = getSelectedView();
         if (selectedView != null) {
@@ -971,7 +975,6 @@
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(AdapterView.class.getName());
         event.setScrollable(isScrollableForAccessibility());
         View selectedView = getSelectedView();
         if (selectedView != null) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 96eb0e2..a242175 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -1084,17 +1084,8 @@
     public void fyiWillBeAdvancedByHostKThx() {
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(AdapterViewAnimator.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(AdapterViewAnimator.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return AdapterViewAnimator.class.getName();
     }
 }
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index a7ba617..01b6530 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -304,17 +304,8 @@
         updateRunning(false);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(AdapterViewFlipper.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(AdapterViewFlipper.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return AdapterViewFlipper.class.getName();
     }
 }
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 90d77f9..9b0d0dd 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -111,17 +111,8 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(Button.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(Button.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return Button.class.getName();
     }
 }
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index d58da8f..5e43916 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -501,16 +501,9 @@
         mDelegate.onConfigurationChanged(newConfig);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        event.setClassName(CalendarView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        info.setClassName(CalendarView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return CalendarView.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index b1fb338..5a7d585 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -72,17 +72,8 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(CheckBox.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(CheckBox.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return CheckBox.class.getName();
     }
 }
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 477862b..344d00a 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -435,11 +435,15 @@
         }
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return CheckedTextView.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(CheckedTextView.class.getName());
         event.setChecked(mChecked);
     }
 
@@ -447,7 +451,6 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(CheckedTextView.class.getName());
         info.setCheckable(true);
         info.setChecked(mChecked);
     }
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 05fc6a1..019d475 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -281,17 +281,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(Chronometer.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(Chronometer.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return Chronometer.class.getName();
     }
 }
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 41a3915..fede493 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -324,11 +324,15 @@
         }
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return CompoundButton.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(CompoundButton.class.getName());
         event.setChecked(mChecked);
     }
 
@@ -336,7 +340,6 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(CompoundButton.class.getName());
         info.setCheckable(true);
         info.setChecked(mChecked);
     }
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 0f0cd2e..7c6055a 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -296,18 +296,9 @@
         mDelegate.onPopulateAccessibilityEvent(event);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        mDelegate.onInitializeAccessibilityEvent(event);
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        mDelegate.onInitializeAccessibilityNodeInfo(info);
+    public CharSequence getAccessibilityClassName() {
+        return DatePicker.class.getName();
     }
 
     @Override
@@ -476,8 +467,6 @@
 
         boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
         void onPopulateAccessibilityEvent(AccessibilityEvent event);
-        void onInitializeAccessibilityEvent(AccessibilityEvent event);
-        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
     }
 
     /**
@@ -892,16 +881,6 @@
             event.getText().add(selectedDateUtterance);
         }
 
-        @Override
-        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-            event.setClassName(DatePicker.class.getName());
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-            info.setClassName(DatePicker.class.getName());
-        }
-
         /**
          * Sets the current locale.
          *
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index a3b834e5..85b4d30 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -584,14 +584,8 @@
         event.getText().add(mCurrentDate.getTime().toString());
     }
 
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        event.setClassName(DatePicker.class.getName());
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        info.setClassName(DatePicker.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return DatePicker.class.getName();
     }
 
     @Override
diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java
index 372bdb3..b936a5b 100644
--- a/core/java/android/widget/DigitalClock.java
+++ b/core/java/android/widget/DigitalClock.java
@@ -115,19 +115,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
+    public CharSequence getAccessibilityClassName() {
         //noinspection deprecation
-        event.setClassName(DigitalClock.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        //noinspection deprecation
-        info.setClassName(DigitalClock.class.getName());
+        return DigitalClock.class.getName();
     }
 }
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index f54beb5..24cc2d8 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -122,18 +122,9 @@
         super.setEllipsize(ellipsis);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(EditText.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(EditText.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return EditText.class.getName();
     }
 
     /** @hide */
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 675dc9bb..323ddb6 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -1341,17 +1341,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ExpandableListView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ExpandableListView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ExpandableListView.class.getName();
     }
 }
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f208fff..57bbc42 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -18,6 +18,7 @@
 
 import java.util.ArrayList;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -203,11 +204,15 @@
     }
 
     @Override
-    @RemotableViewMethod
-    public void setVisibility(@Visibility int visibility) {
-        super.setVisibility(visibility);
-        if (mForeground != null) {
-            mForeground.setVisible(visibility == VISIBLE, false);
+    protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+
+        final Drawable dr = mForeground;
+        if (dr != null) {
+            final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
+            if (visible != dr.isVisible()) {
+                dr.setVisible(visible, false);
+            }
         }
     }
 
@@ -703,19 +708,9 @@
         return new LayoutParams(p);
     }
 
-
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(FrameLayout.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(FrameLayout.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return FrameLayout.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 3c428b0..ac19e6d 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1373,18 +1373,15 @@
 
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(Gallery.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return Gallery.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(Gallery.class.getName());
         info.setScrollable(mItemCount > 1);
         if (isEnabled()) {
             if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) {
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index cc925ebc..41ddc98 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -1190,18 +1190,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(GridLayout.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(GridLayout.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return GridLayout.class.getName();
     }
 
     // Inner classes
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index f7ce57b..8b2217c 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -2341,18 +2341,15 @@
         return result;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(GridView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return GridView.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(GridView.class.getName());
 
         final int columnsCount = getNumColumns();
         final int rowsCount = getCount() / columnsCount;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index c6c979e..b37495f 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -762,18 +762,6 @@
         awakenScrollBars();
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    public void addClickableRectsForAccessibility(List<RectF> outRects) {
-        // This class always consumes touch events, therefore if it
-        // covers a view we do not want to send a click over it.
-        RectF bounds = new RectF();
-        bounds.set(0, 0, getWidth(), getHeight());
-        outRects.add(bounds);
-    }
-
     /** @hide */
     @Override
     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
@@ -807,11 +795,15 @@
         return false;
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return HorizontalScrollView.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(HorizontalScrollView.class.getName());
         final int scrollRange = getScrollRange();
         if (scrollRange > 0) {
             info.setScrollable(true);
@@ -828,7 +820,6 @@
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(HorizontalScrollView.class.getName());
         event.setScrollable(getScrollRange() > 0);
         event.setScrollX(mScrollX);
         event.setScrollY(mScrollY);
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index 3b6825d..22f9c10 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -92,17 +92,8 @@
         return false;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ImageButton.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ImageButton.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ImageButton.class.getName();
     }
 }
diff --git a/core/java/android/widget/ImageSwitcher.java b/core/java/android/widget/ImageSwitcher.java
index 0910eb0..80e908a 100644
--- a/core/java/android/widget/ImageSwitcher.java
+++ b/core/java/android/widget/ImageSwitcher.java
@@ -56,17 +56,8 @@
         showNext();
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ImageSwitcher.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ImageSwitcher.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ImageSwitcher.class.getName();
     }
 }
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index fbad314..757038c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1419,17 +1419,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ImageView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ImageView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ImageView.class.getName();
     }
 }
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 28e50c4..c0f63d2 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -1806,18 +1806,9 @@
         return p instanceof LinearLayout.LayoutParams;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(LinearLayout.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(LinearLayout.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return LinearLayout.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index df0d1fd..0aaef6d 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3878,18 +3878,15 @@
         return false;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ListView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ListView.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ListView.class.getName());
 
         final int rowsCount = getCount();
         final int selectionMode = getSelectionModeForAccessibility();
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 4cafe72..380b328 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -625,18 +625,9 @@
         super.setEnabled(enabled);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(MediaController.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(MediaController.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return MediaController.class.getName();
     }
 
     private View.OnClickListener mRewListener = new View.OnClickListener() {
diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java
index 6954eea..c10e581 100644
--- a/core/java/android/widget/MultiAutoCompleteTextView.java
+++ b/core/java/android/widget/MultiAutoCompleteTextView.java
@@ -202,18 +202,9 @@
         editable.replace(start, end, mTokenizer.terminateToken(text));
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(MultiAutoCompleteTextView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(MultiAutoCompleteTextView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return MultiAutoCompleteTextView.class.getName();
     }
 
     public static interface Tokenizer {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f5cd915..a929f3d 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -27,6 +27,11 @@
 import android.graphics.drawable.StateListDrawable;
 import android.os.Build;
 import android.os.IBinder;
+import android.transition.Transition;
+import android.transition.Transition.EpicenterCallback;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -39,12 +44,13 @@
 import android.view.WindowManager;
 
 import java.lang.ref.WeakReference;
+import java.util.List;
 
 /**
  * <p>A popup window that can be used to display an arbitrary view. The popup
  * window is a floating container that appears on top of the current
  * activity.</p>
- * 
+ *
  * @see android.widget.AutoCompleteTextView
  * @see android.widget.Spinner
  */
@@ -56,7 +62,7 @@
      * it doesn't.
      */
     public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
-    
+
     /**
      * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
      * work with an input method, regardless of whether it is focusable.  This
@@ -64,7 +70,7 @@
      * the input method while it is shown.
      */
     public static final int INPUT_METHOD_NEEDED = 1;
-    
+
     /**
      * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
      * work with an input method, regardless of whether it is focusable.  This
@@ -75,14 +81,32 @@
 
     private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
 
+    /**
+     * Default animation style indicating that separate animations should be
+     * used for top/bottom anchoring states.
+     */
+    private static final int ANIMATION_STYLE_DEFAULT = -1;
+
+    private final int[] mDrawingLocation = new int[2];
+    private final int[] mScreenLocation = new int[2];
+    private final Rect mTempRect = new Rect();
+    private final Rect mAnchorBounds = new Rect();
+
     private Context mContext;
     private WindowManager mWindowManager;
-    
+
     private boolean mIsShowing;
     private boolean mIsDropdown;
 
+    /** View that handles event dispatch and content transitions. */
+    private PopupDecorView mDecorView;
+
+    /** View that holds the popup background. May be the content view. */
+    private View mBackgroundView;
+
+    /** The contents of the popup. */
     private View mContentView;
-    private View mPopupView;
+
     private boolean mFocusable;
     private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
     private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
@@ -112,49 +136,67 @@
 
     private float mElevation;
 
-    private int[] mDrawingLocation = new int[2];
-    private int[] mScreenLocation = new int[2];
-    private Rect mTempRect = new Rect();
-    
     private Drawable mBackground;
     private Drawable mAboveAnchorBackgroundDrawable;
     private Drawable mBelowAnchorBackgroundDrawable;
 
-    // Temporary animation centers. Should be moved into window params?
-    private int mAnchorRelativeX;
-    private int mAnchorRelativeY;
+    private Transition mEnterTransition;
+    private Transition mExitTransition;
 
     private boolean mAboveAnchor;
     private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-    
+
     private OnDismissListener mOnDismissListener;
     private boolean mIgnoreCheekPress = false;
 
-    private int mAnimationStyle = -1;
-    
+    private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+
     private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
         com.android.internal.R.attr.state_above_anchor
     };
 
     private WeakReference<View> mAnchor;
 
-    private final OnScrollChangedListener mOnScrollChangedListener =
-        new OnScrollChangedListener() {
-            @Override
-            public void onScrollChanged() {
-                final View anchor = mAnchor != null ? mAnchor.get() : null;
-                if (anchor != null && mPopupView != null) {
-                    final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
-                            mPopupView.getLayoutParams();
-
-                    updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
-                            mAnchoredGravity));
-                    update(p.x, p.y, -1, -1, true);
-                }
+    private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() {
+        @Override
+        public Rect onGetEpicenter(Transition transition) {
+            final View anchor = mAnchor.get();
+            final View decor = mDecorView;
+            if (anchor == null || decor == null) {
+                return null;
             }
-        };
 
-    private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
+            final Rect anchorBounds = mAnchorBounds;
+            final int[] anchorLocation = mAnchor.get().getLocationOnScreen();
+            final int[] popupLocation = mDecorView.getLocationOnScreen();
+
+            // Compute the position of the anchor relative to the popup.
+            anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight());
+            anchorBounds.offset(anchorLocation[0] - popupLocation[0],
+                    anchorLocation[1] - popupLocation[1]);
+
+            return anchorBounds;
+        }
+    };
+
+    private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
+        @Override
+        public void onScrollChanged() {
+            final View anchor = mAnchor != null ? mAnchor.get() : null;
+            if (anchor != null && mDecorView != null) {
+                final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+                        mDecorView.getLayoutParams();
+
+                updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
+                        mAnchoredGravity));
+                update(p.x, p.y, -1, -1, true);
+            }
+        }
+    };
+
+    private int mAnchorXoff;
+    private int mAnchorYoff;
+    private int mAnchoredGravity;
     private boolean mOverlapAnchor;
 
     private boolean mPopupViewInitialLayoutDirectionInherited;
@@ -185,10 +227,10 @@
     public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
-    
+
     /**
      * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
-     * 
+     *
      * <p>The popup does not provide a background.</p>
      */
     public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@@ -201,11 +243,34 @@
         mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
         mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
 
-        final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
-        mAnimationStyle = animStyle == R.style.Animation_PopupWindow ? -1 : animStyle;
+        // Preserve default behavior from Gingerbread. If the animation is
+        // undefined or explicitly specifies the Gingerbread animation style,
+        // use a sentinel value.
+        if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
+            final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
+            if (animStyle == R.style.Animation_PopupWindow) {
+                mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+            } else {
+                mAnimationStyle = animStyle;
+            }
+        } else {
+            mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+        }
+
+        final Transition enterTransition = getTransition(a.getResourceId(
+                R.styleable.PopupWindow_popupEnterTransition, 0));
+        final Transition exitTransition;
+        if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
+            exitTransition = getTransition(a.getResourceId(
+                    R.styleable.PopupWindow_popupExitTransition, 0));
+        } else {
+            exitTransition = enterTransition == null ? null : enterTransition.clone();
+        }
 
         a.recycle();
 
+        setEnterTransition(enterTransition);
+        setExitTransition(exitTransition);
         setBackgroundDrawable(bg);
     }
 
@@ -286,6 +351,37 @@
         setFocusable(focusable);
     }
 
+    public void setEnterTransition(Transition enterTransition) {
+        mEnterTransition = enterTransition;
+
+        if (mEnterTransition != null) {
+            mEnterTransition.setEpicenterCallback(mEpicenterCallback);
+        }
+    }
+
+    public void setExitTransition(Transition exitTransition) {
+        mExitTransition = exitTransition;
+
+        if (mExitTransition != null) {
+            mExitTransition.setEpicenterCallback(mEpicenterCallback);
+        }
+    }
+
+    private Transition getTransition(int resId) {
+        if (resId != 0 && resId != R.transition.no_transition) {
+            final TransitionInflater inflater = TransitionInflater.from(mContext);
+            final Transition transition = inflater.inflateTransition(resId);
+            if (transition != null) {
+                final boolean isEmpty = transition instanceof TransitionSet
+                        && ((TransitionSet) transition).getTransitionCount() == 0;
+                if (!isEmpty) {
+                    return transition;
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Return the drawable used as the popup window's background.
      *
@@ -379,7 +475,7 @@
      * Set the flag on popup to ignore cheek press events; by default this flag
      * is set to false
      * which means the popup will not ignore cheek press dispatch events.
-     * 
+     *
      * <p>If the popup is showing, calling this method will take effect only
      * the next time the popup is shown or through a manual call to one of
      * the {@link #update()} methods.</p>
@@ -389,7 +485,7 @@
     public void setIgnoreCheekPress() {
         mIgnoreCheekPress = true;
     }
-    
+
 
     /**
      * <p>Change the animation style resource for this popup.</p>
@@ -401,13 +497,13 @@
      * @param animationStyle animation style to use when the popup appears
      *      and disappears.  Set to -1 for the default animation, 0 for no
      *      animation, or a resource identifier for an explicit animation.
-     *      
+     *
      * @see #update()
      */
     public void setAnimationStyle(int animationStyle) {
         mAnimationStyle = animationStyle;
     }
-    
+
     /**
      * <p>Return the view used as the content of the popup window.</p>
      *
@@ -491,7 +587,7 @@
      * @param focusable true if the popup should grab focus, false otherwise.
      *
      * @see #isFocusable()
-     * @see #isShowing() 
+     * @see #isShowing()
      * @see #update()
      */
     public void setFocusable(boolean focusable) {
@@ -500,23 +596,23 @@
 
     /**
      * Return the current value in {@link #setInputMethodMode(int)}.
-     * 
+     *
      * @see #setInputMethodMode(int)
      */
     public int getInputMethodMode() {
         return mInputMethodMode;
-        
+
     }
-    
+
     /**
      * Control how the popup operates with an input method: one of
      * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
      * or {@link #INPUT_METHOD_NOT_NEEDED}.
-     * 
+     *
      * <p>If the popup is showing, calling this method will take effect only
      * the next time the popup is shown or through a manual call to one of
      * the {@link #update()} methods.</p>
-     * 
+     *
      * @see #getInputMethodMode()
      * @see #update()
      */
@@ -547,12 +643,12 @@
     public int getSoftInputMode() {
         return mSoftInputMode;
     }
-    
+
     /**
      * <p>Indicates whether the popup window receives touch events.</p>
-     * 
+     *
      * @return true if the popup is touchable, false otherwise
-     * 
+     *
      * @see #setTouchable(boolean)
      */
     public boolean isTouchable() {
@@ -571,7 +667,7 @@
      * @param touchable true if the popup should receive touch events, false otherwise
      *
      * @see #isTouchable()
-     * @see #isShowing() 
+     * @see #isShowing()
      * @see #update()
      */
     public void setTouchable(boolean touchable) {
@@ -581,9 +677,9 @@
     /**
      * <p>Indicates whether the popup window will be informed of touch events
      * outside of its window.</p>
-     * 
+     *
      * @return true if the popup is outside touchable, false otherwise
-     * 
+     *
      * @see #setOutsideTouchable(boolean)
      */
     public boolean isOutsideTouchable() {
@@ -604,7 +700,7 @@
      * touch events, false otherwise
      *
      * @see #isOutsideTouchable()
-     * @see #isShowing() 
+     * @see #isShowing()
      * @see #update()
      */
     public void setOutsideTouchable(boolean touchable) {
@@ -613,9 +709,9 @@
 
     /**
      * <p>Indicates whether clipping of the popup window is enabled.</p>
-     * 
+     *
      * @return true if the clipping is enabled, false otherwise
-     * 
+     *
      * @see #setClippingEnabled(boolean)
      */
     public boolean isClippingEnabled() {
@@ -626,13 +722,13 @@
      * <p>Allows the popup window to extend beyond the bounds of the screen. By default the
      * window is clipped to the screen boundaries. Setting this to false will allow windows to be
      * accurately positioned.</p>
-     * 
+     *
      * <p>If the popup is showing, calling this method will take effect only
      * the next time the popup is shown or through a manual call to one of
      * the {@link #update()} methods.</p>
      *
      * @param enabled false if the window should be allowed to extend outside of the screen
-     * @see #isShowing() 
+     * @see #isShowing()
      * @see #isClippingEnabled()
      * @see #update()
      */
@@ -660,12 +756,12 @@
     void setAllowScrollingAnchorParent(boolean enabled) {
         mAllowScrollingAnchorParent = enabled;
     }
-    
+
     /**
      * <p>Indicates whether the popup window supports splitting touches.</p>
-     * 
+     *
      * @return true if the touch splitting is enabled, false otherwise
-     * 
+     *
      * @see #setSplitTouchEnabled(boolean)
      */
     public boolean isSplitTouchEnabled() {
@@ -794,7 +890,7 @@
      * window manager by the popup.  By default these are 0, meaning that
      * the current width or height is requested as an explicit size from
      * the window manager.  You can supply
-     * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or 
+     * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
      * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
      * spec supplied instead, replacing the absolute width and height that
      * has been set in the popup.</p>
@@ -815,7 +911,7 @@
         mWidthMode = widthSpec;
         mHeightMode = heightSpec;
     }
-    
+
     /**
      * <p>Return this popup's height MeasureSpec</p>
      *
@@ -836,7 +932,7 @@
      * @param height the height MeasureSpec of the popup
      *
      * @see #getHeight()
-     * @see #isShowing() 
+     * @see #isShowing()
      */
     public void setHeight(int height) {
         mHeight = height;
@@ -847,7 +943,7 @@
      *
      * @return the width MeasureSpec of the popup
      *
-     * @see #setWidth(int) 
+     * @see #setWidth(int)
      */
     public int getWidth() {
         return mWidth;
@@ -913,7 +1009,7 @@
      * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
      * <code>Gravity.LEFT | Gravity.TOP</code>.
      * </p>
-     * 
+     *
      * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
      * @param gravity the gravity which controls the placement of the popup window
      * @param x the popup's x location offset
@@ -939,23 +1035,24 @@
             return;
         }
 
+        TransitionManager.endTransitions(mDecorView);
+
         unregisterForScrollChanged();
 
         mIsShowing = true;
         mIsDropdown = false;
 
-        WindowManager.LayoutParams p = createPopupLayout(token);
-        p.windowAnimations = computeAnimationResource();
-       
+        final WindowManager.LayoutParams p = createPopupLayoutParams(token);
         preparePopup(p);
-        if (gravity == Gravity.NO_GRAVITY) {
-            gravity = Gravity.TOP | Gravity.START;
+
+        // Only override the default if some gravity was specified.
+        if (gravity != Gravity.NO_GRAVITY) {
+            p.gravity = gravity;
         }
-        p.gravity = gravity;
+
         p.x = x;
         p.y = y;
-        if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
-        if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
+
         invokePopup(p);
     }
 
@@ -1021,20 +1118,18 @@
             return;
         }
 
+        TransitionManager.endTransitions(mDecorView);
+
         registerForScrollChanged(anchor, xoff, yoff, gravity);
 
         mIsShowing = true;
         mIsDropdown = true;
 
-        WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
+        final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
         preparePopup(p);
 
-        updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity));
-
-        if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
-        if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
-
-        p.windowAnimations = computeAnimationResource();
+        final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
+        updateAboveAnchor(aboveAnchor);
 
         invokePopup(p);
     }
@@ -1049,12 +1144,12 @@
                 // do the job.
                 if (mAboveAnchorBackgroundDrawable != null) {
                     if (mAboveAnchor) {
-                        mPopupView.setBackground(mAboveAnchorBackgroundDrawable);
+                        mDecorView.setBackground(mAboveAnchorBackgroundDrawable);
                     } else {
-                        mPopupView.setBackground(mBelowAnchorBackgroundDrawable);
+                        mDecorView.setBackground(mBelowAnchorBackgroundDrawable);
                     }
                 } else {
-                    mPopupView.refreshDrawableState();
+                    mDecorView.refreshDrawableState();
                 }
             }
         }
@@ -1076,10 +1171,9 @@
     }
 
     /**
-     * <p>Prepare the popup by embedding in into a new ViewGroup if the
-     * background drawable is not null. If embedding is required, the layout
-     * parameters' height is modified to take into account the background's
-     * padding.</p>
+     * Prepare the popup by embedding it into a new ViewGroup if the background
+     * drawable is not null. If embedding is required, the layout parameters'
+     * height is modified to take into account the background's padding.
      *
      * @param p the layout parameters of the popup's content view
      */
@@ -1089,36 +1183,79 @@
                     + "calling setContentView() before attempting to show the popup.");
         }
 
+        // When a background is available, we embed the content view within
+        // another view that owns the background drawable.
         if (mBackground != null) {
-            final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
-            int height = ViewGroup.LayoutParams.MATCH_PARENT;
-            if (layoutParams != null &&
-                    layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                height = ViewGroup.LayoutParams.WRAP_CONTENT;
-            }
-
-            // when a background is available, we embed the content view
-            // within another view that owns the background drawable
-            PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
-            PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, height
-            );
-            popupViewContainer.setBackground(mBackground);
-            popupViewContainer.addView(mContentView, listParams);
-
-            mPopupView = popupViewContainer;
+            mBackgroundView = createBackgroundView(mContentView);
+            mBackgroundView.setBackground(mBackground);
         } else {
-            mPopupView = mContentView;
+            mBackgroundView = mContentView;
         }
 
-        mPopupView.setElevation(mElevation);
+        mDecorView = createDecorView(mBackgroundView);
+
+        // The background owner should be elevated so that it casts a shadow.
+        mBackgroundView.setElevation(mElevation);
+
+        // We may wrap that in another view, so we'll need to manually specify
+        // the surface insets.
+        final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
+        p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+        p.hasManualSurfaceInsets = true;
+
         mPopupViewInitialLayoutDirectionInherited =
-                (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
+                (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
         mPopupWidth = p.width;
         mPopupHeight = p.height;
     }
 
     /**
+     * Wraps a content view in a PopupViewContainer.
+     *
+     * @param contentView the content view to wrap
+     * @return a PopupViewContainer that wraps the content view
+     */
+    private PopupBackgroundView createBackgroundView(View contentView) {
+        final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
+        final int height;
+        if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+            height = ViewGroup.LayoutParams.WRAP_CONTENT;
+        } else {
+            height = ViewGroup.LayoutParams.MATCH_PARENT;
+        }
+
+        final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
+        final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, height);
+        backgroundView.addView(contentView, listParams);
+
+        return backgroundView;
+    }
+
+    /**
+     * Wraps a content view in a FrameLayout.
+     *
+     * @param contentView the content view to wrap
+     * @return a FrameLayout that wraps the content view
+     */
+    private PopupDecorView createDecorView(View contentView) {
+        final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
+        final int height;
+        if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+            height = ViewGroup.LayoutParams.WRAP_CONTENT;
+        } else {
+            height = ViewGroup.LayoutParams.MATCH_PARENT;
+        }
+
+        final PopupDecorView decorView = new PopupDecorView(mContext);
+        decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
+        decorView.setClipChildren(false);
+        decorView.setClipToPadding(false);
+
+        return decorView;
+    }
+
+    /**
      * <p>Invoke the popup window by adding the content view to the window
      * manager.</p>
      *
@@ -1130,16 +1267,34 @@
         if (mContext != null) {
             p.packageName = mContext.getPackageName();
         }
-        mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
+
+        final View rootView = mContentView.getRootView();
+        rootView.setFitsSystemWindows(mLayoutInsetDecor);
         setLayoutDirectionFromAnchor();
-        mWindowManager.addView(mPopupView, p);
+
+        mWindowManager.addView(rootView, p);
+
+        // Postpone enter transition until the scene root has been laid out.
+        if (mEnterTransition != null) {
+            mEnterTransition.addTarget(mBackgroundView);
+            mEnterTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    transition.removeListener(this);
+                    transition.removeTarget(mBackgroundView);
+                }
+            });
+
+            mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(
+                    new PostLayoutTransitionListener(mDecorView, mEnterTransition));
+        }
     }
 
     private void setLayoutDirectionFromAnchor() {
         if (mAnchor != null) {
             View anchor = mAnchor.get();
             if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
-                mPopupView.setLayoutDirection(anchor.getLayoutDirection());
+                mDecorView.setLayoutDirection(anchor.getLayoutDirection());
             }
         }
     }
@@ -1151,26 +1306,39 @@
      *
      * @return the layout parameters to pass to the window manager
      */
-    private WindowManager.LayoutParams createPopupLayout(IBinder token) {
-        // generates the layout parameters for the drop down
-        // we want a fixed size view located at the bottom left of the anchor
-        WindowManager.LayoutParams p = new WindowManager.LayoutParams();
-        // these gravity settings put the view at the top left corner of the
-        // screen. The view is then positioned to the appropriate location
-        // by setting the x and y offsets to match the anchor's bottom
-        // left corner
+    private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
+        final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
+
+        // These gravity settings put the view at the top left corner of the
+        // screen. The view is then positioned to the appropriate location by
+        // setting the x and y offsets to match the anchor's bottom-left
+        // corner.
         p.gravity = Gravity.START | Gravity.TOP;
-        p.width = mLastWidth = mWidth;
-        p.height = mLastHeight = mHeight;
+        p.flags = computeFlags(p.flags);
+        p.type = mWindowLayoutType;
+        p.token = token;
+        p.softInputMode = mSoftInputMode;
+        p.windowAnimations = computeAnimationResource();
+
         if (mBackground != null) {
             p.format = mBackground.getOpacity();
         } else {
             p.format = PixelFormat.TRANSLUCENT;
         }
-        p.flags = computeFlags(p.flags);
-        p.type = mWindowLayoutType;
-        p.token = token;
-        p.softInputMode = mSoftInputMode;
+
+        if (mHeightMode < 0) {
+            p.height = mLastHeight = mHeightMode;
+        } else {
+            p.height = mLastHeight = mHeight;
+        }
+
+        if (mWidthMode < 0) {
+            p.width = mLastWidth = mWidthMode;
+        } else {
+            p.width = mLastWidth = mWidth;
+        }
+
+        // Used for debugging.
         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
 
         return p;
@@ -1224,7 +1392,7 @@
     }
 
     private int computeAnimationResource() {
-        if (mAnimationStyle == -1) {
+        if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
             if (mIsDropdown) {
                 return mAboveAnchor
                         ? com.android.internal.R.style.Animation_DropDownUp
@@ -1243,7 +1411,7 @@
      * <p>
      * The height must have been set on the layout parameters prior to calling
      * this method.
-     * 
+     *
      * @param anchor the view on which the popup window must be anchored
      * @param p the layout parameters used to display the drop down
      * @param xoff horizontal offset used to adjust for background padding
@@ -1341,19 +1509,15 @@
 
         p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
 
-        // Compute the position of the anchor relative to the popup.
-        mAnchorRelativeX = mDrawingLocation[0] - p.x + anchorHeight / 2;
-        mAnchorRelativeY = mDrawingLocation[1] - p.y + anchorWidth / 2;
-
         return onTop;
     }
-    
+
     /**
      * Returns the maximum height that is available for the popup to be
      * completely shown. It is recommended that this height be the maximum for
      * the popup's height, otherwise it is possible that the popup will be
      * clipped.
-     * 
+     *
      * @param anchor The view on which the popup window must be anchored.
      * @return The maximum available height for the popup to be completely
      *         shown.
@@ -1376,14 +1540,14 @@
     public int getMaxAvailableHeight(View anchor, int yOffset) {
         return getMaxAvailableHeight(anchor, yOffset, false);
     }
-    
+
     /**
      * Returns the maximum height that is available for the popup to be
      * completely shown, optionally ignoring any bottom decorations such as
      * the input method. It is recommended that this height be the maximum for
      * the popup's height, otherwise it is possible that the popup will be
      * clipped.
-     * 
+     *
      * @param anchor The view on which the popup window must be anchored.
      * @param yOffset y offset from the view's bottom edge
      * @param ignoreBottomDecorations if true, the height returned will be
@@ -1391,7 +1555,7 @@
      *        bottom decorations
      * @return The maximum available height for the popup to be completely
      *         shown.
-     *         
+     *
      * @hide Pending API council approval.
      */
     public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
@@ -1400,7 +1564,7 @@
 
         final int[] anchorPos = mDrawingLocation;
         anchor.getLocationOnScreen(anchorPos);
-        
+
         int bottomEdge = displayFrame.bottom;
         if (ignoreBottomDecorations) {
             Resources res = anchor.getContext().getResources();
@@ -1413,49 +1577,83 @@
         int returnedHeight = Math.max(distanceToBottom, distanceToTop);
         if (mBackground != null) {
             mBackground.getPadding(mTempRect);
-            returnedHeight -= mTempRect.top + mTempRect.bottom; 
+            returnedHeight -= mTempRect.top + mTempRect.bottom;
         }
-        
+
         return returnedHeight;
     }
-    
+
     /**
-     * <p>Dispose of the popup window. This method can be invoked only after
-     * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
-     * this method will have no effect.</p>
+     * Disposes of the popup window. This method can be invoked only after
+     * {@link #showAsDropDown(android.view.View)} has been executed. Failing
+     * that, calling this method will have no effect.
      *
-     * @see #showAsDropDown(android.view.View) 
+     * @see #showAsDropDown(android.view.View)
      */
     public void dismiss() {
-        if (isShowing() && mPopupView != null) {
-            mIsShowing = false;
+        if (!isShowing()) {
+            return;
+        }
 
-            unregisterForScrollChanged();
+        unregisterForScrollChanged();
 
-            try {
-                mWindowManager.removeViewImmediate(mPopupView);
-            } finally {
-                if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
-                    ((ViewGroup) mPopupView).removeView(mContentView);
+        mIsShowing = false;
+
+        if (mExitTransition != null) {
+            // Cache the content view, since it may change without notice.
+            final View contentView = mContentView;
+
+            mExitTransition.addTarget(mBackgroundView);
+            mExitTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    transition.removeListener(this);
+                    transition.removeTarget(mBackgroundView);
+
+                    dismissImmediate(contentView);
                 }
-                mPopupView = null;
+            });
 
-                if (mOnDismissListener != null) {
-                    mOnDismissListener.onDismiss();
-                }
+            TransitionManager.beginDelayedTransition(mDecorView, mExitTransition);
+
+            // Transition to invisible.
+            mBackgroundView.setVisibility(View.INVISIBLE);
+        } else {
+            dismissImmediate(mContentView);
+        }
+
+        if (mOnDismissListener != null) {
+            mOnDismissListener.onDismiss();
+        }
+    }
+
+    /**
+     * Removes the popup from the window manager and tears down the supporting
+     * view hierarchy, if necessary.
+     */
+    private void dismissImmediate(View contentView) {
+        try {
+            mWindowManager.removeViewImmediate(mDecorView);
+        } finally {
+            mDecorView.removeView(mBackgroundView);
+            mDecorView = null;
+
+            if (mBackgroundView != contentView) {
+                ((ViewGroup) mBackgroundView).removeView(contentView);
             }
+            mBackgroundView = null;
         }
     }
 
     /**
      * Sets the listener to be called when the window is dismissed.
-     * 
+     *
      * @param onDismissListener The listener.
      */
     public void setOnDismissListener(OnDismissListener onDismissListener) {
         mOnDismissListener = onDismissListener;
     }
-    
+
     /**
      * Updates the state of the popup window, if it is currently being displayed,
      * from the currently set state.  This includes:
@@ -1467,12 +1665,12 @@
         if (!isShowing() || mContentView == null) {
             return;
         }
-        
-        WindowManager.LayoutParams p = (WindowManager.LayoutParams)
-                mPopupView.getLayoutParams();
-        
+
+        final WindowManager.LayoutParams p =
+                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+
         boolean update = false;
-        
+
         final int newAnim = computeAnimationResource();
         if (newAnim != p.windowAnimations) {
             p.windowAnimations = newAnim;
@@ -1487,7 +1685,7 @@
 
         if (update) {
             setLayoutDirectionFromAnchor();
-            mWindowManager.updateViewLayout(mPopupView, p);
+            mWindowManager.updateViewLayout(mDecorView, p);
         }
     }
 
@@ -1500,11 +1698,11 @@
      * @param height the new height
      */
     public void update(int width, int height) {
-        WindowManager.LayoutParams p = (WindowManager.LayoutParams)
-                mPopupView.getLayoutParams();
+        final WindowManager.LayoutParams p =
+                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
         update(p.x, p.y, width, height, false);
     }
-    
+
     /**
      * <p>Updates the position and the dimension of the popup window. Width and
      * height can be set to -1 to update location only.  Calling this function
@@ -1548,7 +1746,8 @@
             return;
         }
 
-        WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
+        final WindowManager.LayoutParams p =
+                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
 
         boolean update = force;
 
@@ -1588,7 +1787,7 @@
 
         if (update) {
             setLayoutDirectionFromAnchor();
-            mWindowManager.updateViewLayout(mPopupView, p);
+            mWindowManager.updateViewLayout(mDecorView, p);
         }
     }
 
@@ -1655,7 +1854,7 @@
         }
 
         final WindowManager.LayoutParams p =
-                (WindowManager.LayoutParams) mPopupView.getLayoutParams();
+                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
         final int x = p.x;
         final int y = p.y;
         if (updateLocation) {
@@ -1679,23 +1878,22 @@
     }
 
     private void unregisterForScrollChanged() {
-        WeakReference<View> anchorRef = mAnchor;
-        View anchor = null;
-        if (anchorRef != null) {
-            anchor = anchorRef.get();
-        }
+        final WeakReference<View> anchorRef = mAnchor;
+        final View anchor = anchorRef == null ? null : anchorRef.get();
         if (anchor != null) {
-            ViewTreeObserver vto = anchor.getViewTreeObserver();
+            final ViewTreeObserver vto = anchor.getViewTreeObserver();
             vto.removeOnScrollChangedListener(mOnScrollChangedListener);
         }
+
         mAnchor = null;
     }
 
     private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
         unregisterForScrollChanged();
 
-        mAnchor = new WeakReference<View>(anchor);
-        ViewTreeObserver vto = anchor.getViewTreeObserver();
+        mAnchor = new WeakReference<>(anchor);
+
+        final ViewTreeObserver vto = anchor.getViewTreeObserver();
         if (vto != null) {
             vto.addOnScrollChangedListener(mOnScrollChangedListener);
         }
@@ -1705,23 +1903,49 @@
         mAnchoredGravity = gravity;
     }
 
-    private class PopupViewContainer extends FrameLayout {
-        private static final String TAG = "PopupWindow.PopupViewContainer";
+    /**
+     * Layout listener used to run a transition immediately after a view is
+     * laid out. Forces the view to transition from invisible to visible.
+     */
+    private static class PostLayoutTransitionListener implements
+            ViewTreeObserver.OnGlobalLayoutListener {
+        private final ViewGroup mSceneRoot;
+        private final Transition mTransition;
 
-        public PopupViewContainer(Context context) {
-            super(context);
+        public PostLayoutTransitionListener(ViewGroup sceneRoot, Transition transition) {
+            mSceneRoot = sceneRoot;
+            mTransition = transition;
         }
 
         @Override
-        protected int[] onCreateDrawableState(int extraSpace) {
-            if (mAboveAnchor) {
-                // 1 more needed for the above anchor state
-                final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
-                View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
-                return drawableState;
-            } else {
-                return super.onCreateDrawableState(extraSpace);
+        public void onGlobalLayout() {
+            final ViewTreeObserver observer = mSceneRoot.getViewTreeObserver();
+            if (observer == null) {
+                // View has been detached.
+                return;
             }
+
+            observer.removeOnGlobalLayoutListener(this);
+
+            // Set all targets to be initially invisible.
+            final List<View> targets = mTransition.getTargets();
+            final int N = targets.size();
+            for (int i = 0; i < N; i++) {
+                targets.get(i).setVisibility(View.INVISIBLE);
+            }
+
+            TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+
+            // Transition targets to visible.
+            for (int i = 0; i < N; i++) {
+                targets.get(i).setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
+    private class PopupDecorView extends FrameLayout {
+        public PopupDecorView(Context context) {
+            super(context);
         }
 
         @Override
@@ -1731,15 +1955,14 @@
                     return super.dispatchKeyEvent(event);
                 }
 
-                if (event.getAction() == KeyEvent.ACTION_DOWN
-                        && event.getRepeatCount() == 0) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
+                if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+                    final KeyEvent.DispatcherState state = getKeyDispatcherState();
                     if (state != null) {
                         state.startTracking(event, this);
                     }
                     return true;
                 } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
+                    final KeyEvent.DispatcherState state = getKeyDispatcherState();
                     if (state != null && state.isTracking(event) && !event.isCanceled()) {
                         dismiss();
                         return true;
@@ -1763,7 +1986,7 @@
         public boolean onTouchEvent(MotionEvent event) {
             final int x = (int) event.getX();
             final int y = (int) event.getY();
-            
+
             if ((event.getAction() == MotionEvent.ACTION_DOWN)
                     && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                 dismiss();
@@ -1775,17 +1998,22 @@
                 return super.onTouchEvent(event);
             }
         }
+    }
 
-    /** @hide */
+    private class PopupBackgroundView extends FrameLayout {
+        public PopupBackgroundView(Context context) {
+            super(context);
+        }
+
         @Override
-        public void sendAccessibilityEventInternal(int eventType) {
-            // clinets are interested in the content not the container, make it event source
-            if (mContentView != null) {
-                mContentView.sendAccessibilityEvent(eventType);
+        protected int[] onCreateDrawableState(int extraSpace) {
+            if (mAboveAnchor) {
+                final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+                View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
+                return drawableState;
             } else {
-                super.sendAccessibilityEventInternal(eventType);
+                return super.onCreateDrawableState(extraSpace);
             }
         }
     }
-    
 }
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index d32cb10..9c3296b 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1825,20 +1825,17 @@
         mAttached = false;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ProgressBar.class.getName());
-        event.setItemCount(mMax);
-        event.setCurrentItemIndex(mProgress);
+    public CharSequence getAccessibilityClassName() {
+        return ProgressBar.class.getName();
     }
 
     /** @hide */
     @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ProgressBar.class.getName());
+    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEventInternal(event);
+        event.setItemCount(mMax);
+        event.setCurrentItemIndex(mProgress);
     }
 
     /**
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index d0e8081..3068de9 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -304,18 +304,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(QuickContactBadge.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(QuickContactBadge.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return QuickContactBadge.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 094a712..4b061d3 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -1271,11 +1271,19 @@
         @Override
         protected int getVirtualViewAt(float x, float y) {
             final int id;
+
+            // Calling getDegreesXY() has side-effects, so we need to cache the
+            // current inner circle value and restore after the call.
+            final boolean wasOnInnerCircle = mIsOnInnerCircle;
             final int degrees = getDegreesFromXY(x, y, true);
+            final boolean isOnInnerCircle = mIsOnInnerCircle;
+            mIsOnInnerCircle = wasOnInnerCircle;
+
             if (degrees != -1) {
                 final int snapDegrees = snapOnly30s(degrees, 0) % 360;
                 if (mShowHours) {
-                    final int hour = getHourForDegrees(snapDegrees, mIsOnInnerCircle);
+                    final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle);
+                    final int hour = mIs24HourMode ? hour24 : hour24To12(hour24);
                     id = makeId(TYPE_HOUR, hour);
                 } else {
                     final int current = getCurrentMinute();
@@ -1404,6 +1412,16 @@
             return hour24;
         }
 
+        private int hour24To12(int hour24) {
+            if (hour24 == 0) {
+                return 12;
+            } else if (hour24 > 12) {
+                return hour24 - 12;
+            } else {
+                return hour24;
+            }
+        }
+
         private void getBoundsForVirtualView(int virtualViewId, Rect bounds) {
             final float radius;
             final int type = getTypeFromId(virtualViewId);
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 82280b4..aebc1b6 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -79,17 +79,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(RadioButton.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(RadioButton.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return RadioButton.class.getName();
     }
 }
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index c0f60eb..f04bb3d 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -240,18 +240,9 @@
         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(RadioGroup.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(RadioGroup.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return RadioGroup.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 2d0649d..4268961 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -330,18 +330,9 @@
         
         super.setMax(max);
     }
-    
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(RatingBar.class.getName());
-    }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(RatingBar.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return RatingBar.class.getName();
     }
 }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 71f4da0..89b1d54 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1103,18 +1103,9 @@
         return false;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(RelativeLayout.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(RelativeLayout.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return RelativeLayout.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index b12c581..6fd90c3 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -44,6 +44,7 @@
     private boolean mRangeChanged;
     private boolean mAlwaysDrawHorizontalTrack;
     private boolean mAlwaysDrawVerticalTrack;
+    private boolean mMutated;
 
     private int mAlpha = 255;
     private boolean mHasSetAlpha;
@@ -266,6 +267,10 @@
 
     private void propagateCurrentState(Drawable d) {
         if (d != null) {
+            if (mMutated) {
+                d.mutate();
+            }
+
             d.setState(getState());
             d.setCallback(this);
 
@@ -290,6 +295,26 @@
     }
 
     @Override
+    public ScrollBarDrawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            if (mVerticalTrack != null) {
+                mVerticalTrack.mutate();
+            }
+            if (mVerticalThumb != null) {
+                mVerticalThumb.mutate();
+            }
+            if (mHorizontalTrack != null) {
+                mHorizontalTrack.mutate();
+            }
+            if (mHorizontalThumb != null) {
+                mHorizontalThumb.mutate();
+            }
+            mMutated = true;
+        }
+        return this;
+    }
+
+    @Override
     public void setAlpha(int alpha) {
         mAlpha = alpha;
         mHasSetAlpha = true;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1098419..b95c27d 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -839,11 +839,15 @@
         return false;
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return ScrollView.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ScrollView.class.getName());
         if (isEnabled()) {
             final int scrollRange = getScrollRange();
             if (scrollRange > 0) {
@@ -862,7 +866,6 @@
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ScrollView.class.getName());
         final boolean scrollable = getScrollRange() > 0;
         event.setScrollable(scrollable);
         event.setScrollX(mScrollX);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index a1f361a..8846421 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1325,18 +1325,9 @@
         setIconified(false);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(SearchView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(SearchView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return SearchView.class.getName();
     }
 
     private void adjustDropDownSizeAndPosition() {
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index aa5c055..e97bdf25 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -124,17 +124,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(SeekBar.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(SeekBar.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return SeekBar.class.getName();
     }
 }
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index e3f4a10..272f4b0 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -837,18 +837,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(SlidingDrawer.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(SlidingDrawer.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return SlidingDrawer.class.getName();
     }
 
     private void closeDrawer() {
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index a6e0e6d..6ee2b4c 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -765,18 +765,15 @@
         dialog.dismiss();
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(Spinner.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return Spinner.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(Spinner.class.getName());
 
         if (mAdapter != null) {
             info.setCanOpenPopup(true);
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 2aa1c09..803ba4b 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -1224,18 +1224,15 @@
         measureChildren();
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(StackView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return StackView.class.getName();
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(StackView.class.getName());
         info.setScrollable(getChildCount() > 1);
         if (isEnabled()) {
             if (getDisplayedChild() < getChildCount() - 1) {
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 13d6b42..b959ddc 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -24,10 +25,12 @@
 import android.graphics.Canvas;
 import android.graphics.Insets;
 import android.graphics.Paint;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.text.Layout;
 import android.text.StaticLayout;
 import android.text.TextPaint;
@@ -41,6 +44,7 @@
 import android.view.MotionEvent;
 import android.view.SoundEffectConstants;
 import android.view.VelocityTracker;
+import android.view.ViewAssistData;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -84,7 +88,17 @@
     private static final int MONOSPACE = 3;
 
     private Drawable mThumbDrawable;
+    private ColorStateList mThumbTintList = null;
+    private PorterDuff.Mode mThumbTintMode = null;
+    private boolean mHasThumbTint = false;
+    private boolean mHasThumbTintMode = false;
+
     private Drawable mTrackDrawable;
+    private ColorStateList mTrackTintList = null;
+    private PorterDuff.Mode mTrackTintMode = null;
+    private boolean mHasTrackTint = false;
+    private boolean mHasTrackTintMode = false;
+
     private int mThumbTextPadding;
     private int mSwitchMinWidth;
     private int mSwitchPadding;
@@ -473,6 +487,86 @@
     }
 
     /**
+     * Applies a tint to the track drawable. Does not modify the current
+     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link #setTrackDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and tint
+     * mode using {@link Drawable#setTintList(ColorStateList)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#Switch_trackTint
+     * @see #getTrackTintList()
+     * @see Drawable#setTintList(ColorStateList)
+     */
+    public void setTrackTintList(@Nullable ColorStateList tint) {
+        mTrackTintList = tint;
+        mHasTrackTint = true;
+
+        applyTrackTint();
+    }
+
+    /**
+     * @return the tint applied to the track drawable
+     * @attr ref android.R.styleable#Switch_trackTint
+     * @see #setTrackTintList(ColorStateList)
+     */
+    @Nullable
+    public ColorStateList getTrackTintList() {
+        return mTrackTintList;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setTrackTintList(ColorStateList)}} to the track drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#Switch_trackTintMode
+     * @see #getTrackTintMode()
+     * @see Drawable#setTintMode(PorterDuff.Mode)
+     */
+    public void setTrackTintMode(@Nullable PorterDuff.Mode tintMode) {
+        mTrackTintMode = tintMode;
+        mHasTrackTintMode = true;
+
+        applyTrackTint();
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the track
+     *         drawable
+     * @attr ref android.R.styleable#Switch_trackTintMode
+     * @see #setTrackTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getTrackTintMode() {
+        return mTrackTintMode;
+    }
+
+    private void applyTrackTint() {
+        if (mTrackDrawable != null && (mHasTrackTint || mHasTrackTintMode)) {
+            mTrackDrawable = mTrackDrawable.mutate();
+
+            if (mHasTrackTint) {
+                mTrackDrawable.setTintList(mTrackTintList);
+            }
+
+            if (mHasTrackTintMode) {
+                mTrackDrawable.setTintMode(mTrackTintMode);
+            }
+
+            // The drawable (or one of its children) may not have been
+            // stateful before applying the tint, so let's try again.
+            if (mTrackDrawable.isStateful()) {
+                mTrackDrawable.setState(getDrawableState());
+            }
+        }
+    }
+
+    /**
      * Set the drawable used for the switch "thumb" - the piece that the user
      * can physically touch and drag along the track.
      *
@@ -516,6 +610,86 @@
     }
 
     /**
+     * Applies a tint to the thumb drawable. Does not modify the current
+     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link #setThumbDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and tint
+     * mode using {@link Drawable#setTintList(ColorStateList)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#Switch_thumbTint
+     * @see #getThumbTintList()
+     * @see Drawable#setTintList(ColorStateList)
+     */
+    public void setThumbTintList(@Nullable ColorStateList tint) {
+        mThumbTintList = tint;
+        mHasThumbTint = true;
+
+        applyThumbTint();
+    }
+
+    /**
+     * @return the tint applied to the thumb drawable
+     * @attr ref android.R.styleable#Switch_thumbTint
+     * @see #setThumbTintList(ColorStateList)
+     */
+    @Nullable
+    public ColorStateList getThumbTintList() {
+        return mThumbTintList;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setThumbTintList(ColorStateList)}} to the thumb drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#Switch_thumbTintMode
+     * @see #getThumbTintMode()
+     * @see Drawable#setTintMode(PorterDuff.Mode)
+     */
+    public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
+        mThumbTintMode = tintMode;
+        mHasThumbTintMode = true;
+
+        applyThumbTint();
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the thumb
+     *         drawable
+     * @attr ref android.R.styleable#Switch_thumbTintMode
+     * @see #setThumbTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getThumbTintMode() {
+        return mThumbTintMode;
+    }
+
+    private void applyThumbTint() {
+        if (mThumbDrawable != null && (mHasThumbTint || mHasThumbTintMode)) {
+            mThumbDrawable = mThumbDrawable.mutate();
+
+            if (mHasThumbTint) {
+                mThumbDrawable.setTintList(mThumbTintList);
+            }
+
+            if (mHasThumbTintMode) {
+                mThumbDrawable.setTintMode(mThumbTintMode);
+            }
+
+            // The drawable (or one of its children) may not have been
+            // stateful before applying the tint, so let's try again.
+            if (mThumbDrawable.isStateful()) {
+                mThumbDrawable.setState(getDrawableState());
+            }
+        }
+    }
+
+    /**
      * Specifies whether the track should be split by the thumb. When true,
      * the thumb's optical bounds will be clipped out of the track drawable,
      * then the thumb will be drawn into the resulting gap.
@@ -1181,18 +1355,31 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(Switch.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return Switch.class.getName();
+    }
+
+    @Override
+    public void onProvideAssistData(ViewAssistData data, Bundle extras) {
+        super.onProvideAssistData(data, extras);
+        CharSequence switchText = isChecked() ? mTextOn : mTextOff;
+        if (!TextUtils.isEmpty(switchText)) {
+            CharSequence oldText = data.getText();
+            if (TextUtils.isEmpty(oldText)) {
+                data.setText(switchText);
+            } else {
+                StringBuilder newText = new StringBuilder();
+                newText.append(oldText).append(' ').append(switchText);
+                data.setText(newText);
+            }
+        }
     }
 
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(Switch.class.getName());
         CharSequence switchText = isChecked() ? mTextOn : mTextOff;
         if (!TextUtils.isEmpty(switchText)) {
             CharSequence oldText = info.getText();
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 3c3389a..bf35cf9 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -384,18 +384,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(TabHost.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(TabHost.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return TabHost.class.getName();
     }
 
     public void setCurrentTab(int index) {
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index bddee91..88ecb13 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -415,11 +415,15 @@
         return false;
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return TabWidget.class.getName();
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(TabWidget.class.getName());
         event.setItemCount(getTabCount());
         event.setCurrentItemIndex(mSelectedTab);
     }
@@ -436,13 +440,6 @@
         super.sendAccessibilityEventUncheckedInternal(event);
     }
 
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(TabWidget.class.getName());
-    }
-
     /**
      * Sets the current tab and focuses the UI on it.
      * This method makes sure that the focused tab matches the selected
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index a6b78a4..c825d17 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -666,18 +666,9 @@
         return new LayoutParams(p);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(TableLayout.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(TableLayout.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return TableLayout.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index c29296a..72fce3f 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -379,18 +379,9 @@
         return new LayoutParams(p);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(TableRow.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(TableRow.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return TableRow.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/TextSwitcher.java b/core/java/android/widget/TextSwitcher.java
index 7c883ba..22822b1 100644
--- a/core/java/android/widget/TextSwitcher.java
+++ b/core/java/android/widget/TextSwitcher.java
@@ -91,17 +91,8 @@
         ((TextView)getCurrentView()).setText(text);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(TextSwitcher.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(TextSwitcher.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return TextSwitcher.class.getName();
     }
 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e8bf623..848c1c0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -109,6 +109,7 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewAssistData;
 import android.view.ViewConfiguration;
 import android.view.ViewDebug;
 import android.view.ViewGroup.LayoutParams;
@@ -665,6 +666,7 @@
         ColorStateList textColorLink = null;
         int textSize = 15;
         String fontFamily = null;
+        boolean fontFamilyExplicit = false;
         int typefaceIndex = -1;
         int styleIndex = -1;
         boolean allCaps = false;
@@ -1029,6 +1031,7 @@
 
             case com.android.internal.R.styleable.TextView_fontFamily:
                 fontFamily = a.getString(attr);
+                fontFamilyExplicit = true;
                 break;
 
             case com.android.internal.R.styleable.TextView_password:
@@ -1333,6 +1336,9 @@
             typefaceIndex = MONOSPACE;
         }
 
+        if (typefaceIndex != -1 && !fontFamilyExplicit) {
+            fontFamily = null;
+        }
         setTypefaceFromAttrs(fontFamily, typefaceIndex, styleIndex);
 
         if (shadowcolor != 0) {
@@ -8526,12 +8532,26 @@
                 UserHandle.USER_CURRENT_OR_SELF) == 1);
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return TextView.class.getName();
+    }
+
+    @Override
+    public void onProvideAssistData(ViewAssistData data, Bundle extras) {
+        super.onProvideAssistData(data, extras);
+        final boolean isPassword = hasPasswordTransformationMethod();
+        if (!isPassword) {
+            data.setText(getText(), getSelectionStart(), getSelectionEnd());
+        }
+        data.setHint(getHint());
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
 
-        event.setClassName(TextView.class.getName());
         final boolean isPassword = hasPasswordTransformationMethod();
         event.setPassword(isPassword);
 
@@ -8547,7 +8567,6 @@
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
 
-        info.setClassName(TextView.class.getName());
         final boolean isPassword = hasPasswordTransformationMethod();
         info.setPassword(isPassword);
 
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index bbf5f53..9df8a21 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -195,33 +195,17 @@
         mDelegate.onRestoreInstanceState(ss);
     }
 
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return TimePicker.class.getName();
+    }
+
     /** @hide */
     @Override
     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
         return mDelegate.dispatchPopulateAccessibilityEvent(event);
     }
 
-    /** @hide */
-    @Override
-    public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onPopulateAccessibilityEventInternal(event);
-        mDelegate.onPopulateAccessibilityEvent(event);
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        mDelegate.onInitializeAccessibilityEvent(event);
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        mDelegate.onInitializeAccessibilityNodeInfo(info);
-    }
-
     /**
      * A delegate interface that defined the public API of the TimePicker. Allows different
      * TimePicker implementations. This would need to be implemented by the TimePicker delegates
@@ -252,8 +236,6 @@
 
         boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
         void onPopulateAccessibilityEvent(AccessibilityEvent event);
-        void onInitializeAccessibilityEvent(AccessibilityEvent event);
-        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
     }
 
     /**
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 1534429..05c7a5f 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -466,16 +466,6 @@
         event.getText().add(selectedDate);
     }
 
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        event.setClassName(TimePicker.class.getName());
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        info.setClassName(TimePicker.class.getName());
-    }
-
     /**
      * Set whether in keyboard mode or not.
      *
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index e162f4a..af69110 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -427,16 +427,6 @@
         event.getText().add(selectedDateUtterance);
     }
 
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        event.setClassName(TimePicker.class.getName());
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        info.setClassName(TimePicker.class.getName());
-    }
-
     private void updateInputState() {
         // Make sure that if the user changes the value and the IME is active
         // for one of the inputs if this widget, the IME is closed. If the user
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index 1b23778..4f8342b 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -153,17 +153,8 @@
         }
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ToggleButton.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ToggleButton.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ToggleButton.class.getName();
     }
 }
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 0f35e0d..c5325c4 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1101,18 +1101,6 @@
      * @hide
      */
     @Override
-    public void addClickableRectsForAccessibility(List<RectF> outRects) {
-        // This class always consumes touch events, therefore if it
-        // covers a view we do not want to send a click over it.
-        RectF bounds = new RectF();
-        bounds.set(0, 0, getWidth(), getHeight());
-        outRects.add(bounds);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
     protected void onSetLayoutParams(View child, ViewGroup.LayoutParams lp) {
         /*
          * Apps may set ActionBar.LayoutParams on their action bar custom views when
diff --git a/core/java/android/widget/TwoLineListItem.java b/core/java/android/widget/TwoLineListItem.java
index 9035dbe..0cd7eb9 100644
--- a/core/java/android/widget/TwoLineListItem.java
+++ b/core/java/android/widget/TwoLineListItem.java
@@ -93,17 +93,8 @@
         return mText2;
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(TwoLineListItem.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(TwoLineListItem.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return TwoLineListItem.class.getName();
     }
 }
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index a240dc2..48283d4 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -201,18 +201,9 @@
         setMeasuredDimension(width, height);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(VideoView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(VideoView.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return VideoView.class.getName();
     }
 
     public int resolveAdjustedSize(int desiredSize, int measureSpec) {
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index d31754b..5ef5222 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -357,17 +357,8 @@
         return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ViewAnimator.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ViewAnimator.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ViewAnimator.class.getName();
     }
 }
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 587f469..a43a185 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -149,18 +149,9 @@
         updateRunning();
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ViewFlipper.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ViewFlipper.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ViewFlipper.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/ViewSwitcher.java b/core/java/android/widget/ViewSwitcher.java
index c97770f..2f544cc 100644
--- a/core/java/android/widget/ViewSwitcher.java
+++ b/core/java/android/widget/ViewSwitcher.java
@@ -68,18 +68,9 @@
         super.addView(child, index, params);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ViewSwitcher.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ViewSwitcher.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ViewSwitcher.class.getName();
     }
 
     /**
diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java
index e0be0ab..6b3faed 100644
--- a/core/java/android/widget/ZoomButton.java
+++ b/core/java/android/widget/ZoomButton.java
@@ -103,17 +103,8 @@
         return super.dispatchUnhandledMove(focused, direction);
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ZoomButton.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ZoomButton.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ZoomButton.class.getName();
     }
 }
diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java
index a0aacea..bef1ace 100644
--- a/core/java/android/widget/ZoomControls.java
+++ b/core/java/android/widget/ZoomControls.java
@@ -109,17 +109,8 @@
         return mZoomIn.hasFocus() || mZoomOut.hasFocus();
     }
 
-    /** @hide */
     @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setClassName(ZoomControls.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.setClassName(ZoomControls.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ZoomControls.class.getName();
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index aba4bd0..c9b44be 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -800,7 +800,12 @@
             if (args.niceName != null) {
                 String property = "wrap." + args.niceName;
                 if (property.length() > 31) {
-                    property = property.substring(0, 31);
+                    // Properties with a trailing "." are illegal.
+                    if (property.charAt(30) != '.') {
+                        property = property.substring(0, 31);
+                    } else {
+                        property = property.substring(0, 30);
+                    }
                 }
                 args.invokeWith = SystemProperties.get(property);
                 if (args.invokeWith != null && args.invokeWith.length() == 0) {
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
new file mode 100644
index 0000000..d8a7f16
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterClipReveal.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
+ * after the scene change and animates between those and the epicenter bounds
+ * during a visibility transition.
+ */
+public class EpicenterClipReveal extends Visibility {
+    private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+    private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+
+    public EpicenterClipReveal() {}
+
+    public EpicenterClipReveal(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        super.captureEndValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    private void captureValues(TransitionValues values) {
+        final View view = values.view;
+        if (view.getVisibility() == View.GONE) {
+            return;
+        }
+
+        final Rect clip = view.getClipBounds();
+        values.values.put(PROPNAME_CLIP, clip);
+
+        if (clip == null) {
+            final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+            values.values.put(PROPNAME_BOUNDS, bounds);
+        }
+    }
+
+    @Override
+    public Animator onAppear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (endValues == null) {
+            return null;
+        }
+
+        final Rect start = getEpicenter();
+        final Rect end = getBestRect(endValues);
+
+        // Prepare the view.
+        view.setClipBounds(start);
+
+        return createRectAnimator(view, start, end);
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null) {
+            return null;
+        }
+
+        final Rect start = getBestRect(startValues);
+        final Rect end = getEpicenter();
+
+        // Prepare the view.
+        view.setClipBounds(start);
+
+        return createRectAnimator(view, start, end);
+    }
+
+    private Rect getBestRect(TransitionValues values) {
+        final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+        if (clipRect == null) {
+            return (Rect) values.values.get(PROPNAME_BOUNDS);
+        }
+        return clipRect;
+    }
+
+    private Animator createRectAnimator(View view, Rect start, Rect end) {
+        final RectEvaluator evaluator = new RectEvaluator(new Rect());
+        return ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end);
+    }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index e8e2c8d..7937a95 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -227,18 +227,6 @@
         return true;
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    public void addClickableRectsForAccessibility(List<RectF> outRects) {
-        // This class always consumes touch events, therefore if it
-        // covers a view we do not want to send a click over it.
-        RectF bounds = new RectF();
-        bounds.set(0, 0, getWidth(), getHeight());
-        outRects.add(bounds);
-    }
-
     @Override
     public boolean onHoverEvent(MotionEvent ev) {
         super.onHoverEvent(ev);
diff --git a/core/java/com/android/internal/widget/FaceUnlockView.java b/core/java/com/android/internal/widget/FaceUnlockView.java
deleted file mode 100644
index 121e601..0000000
--- a/core/java/com/android/internal/widget/FaceUnlockView.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.RelativeLayout;
-
-public class FaceUnlockView extends RelativeLayout {
-    private static final String TAG = "FaceUnlockView";
-
-    public FaceUnlockView(Context context) {
-        this(context, null);
-    }
-
-    public FaceUnlockView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    private int resolveMeasured(int measureSpec, int desired)
-    {
-        int result = 0;
-        int specSize = MeasureSpec.getSize(measureSpec);
-        switch (MeasureSpec.getMode(measureSpec)) {
-            case MeasureSpec.UNSPECIFIED:
-                result = desired;
-                break;
-            case MeasureSpec.AT_MOST:
-                result = Math.max(specSize, desired);
-                break;
-            case MeasureSpec.EXACTLY:
-            default:
-                result = specSize;
-        }
-        return result;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int minimumWidth = getSuggestedMinimumWidth();
-        final int minimumHeight = getSuggestedMinimumHeight();
-        int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
-        int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
-        final int chosenSize = Math.min(viewWidth, viewHeight);
-        final int newWidthMeasureSpec =
-                MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST);
-        final int newHeightMeasureSpec =
-                MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST);
-
-        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
-    }
-}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index ec01703..90821dc 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -18,7 +18,6 @@
 
 import android.Manifest;
 import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
 import android.content.ComponentName;
@@ -37,18 +36,12 @@
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
-import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
-import android.widget.Button;
 
-import com.android.internal.R;
 import com.google.android.collect.Lists;
 
 import java.nio.charset.StandardCharsets;
-import libcore.util.HexEncoding;
-
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -56,6 +49,8 @@
 import java.util.Collection;
 import java.util.List;
 
+import libcore.util.HexEncoding;
+
 /**
  * Utilities for the lock pattern and its settings.
  */
@@ -100,6 +95,11 @@
     public static final int MIN_LOCK_PATTERN_SIZE = 4;
 
     /**
+     * The minimum size of a valid password.
+     */
+    public static final int MIN_LOCK_PASSWORD_SIZE = 4;
+
+    /**
      * The minimum number of dots the user must include in a wrong pattern
      * attempt for it to be counted against the counts that affect
      * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET}
@@ -489,8 +489,9 @@
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) {
         try {
-            if (pattern == null) {
-                throw new IllegalArgumentException("pattern must not be null");
+            if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
+                throw new IllegalArgumentException("pattern must not be null and at least "
+                        + MIN_LOCK_PATTERN_SIZE + " dots long.");
             }
 
             getLockSettings().setLockPattern(patternToString(pattern), userId);
@@ -701,8 +702,9 @@
     public void saveLockPassword(String password, int quality, int userHandle) {
         try {
             DevicePolicyManager dpm = getDevicePolicyManager();
-            if (TextUtils.isEmpty(password)) {
-                throw new IllegalArgumentException("password must not be null nor empty");
+            if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
+                throw new IllegalArgumentException("password must not be null and at least "
+                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
             }
 
             getLockSettings().setLockPassword(password, userHandle);
@@ -1070,21 +1072,6 @@
         return deadline;
     }
 
-    public boolean isEmergencyCallCapable() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_voice_capable);
-    }
-
-    public boolean isPukUnlockScreenEnable() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_enable_puk_unlock_screen);
-    }
-
-    public boolean isEmergencyCallEnabledWhileSimLocked() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
-    }
-
     private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
         try {
             return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
@@ -1136,58 +1123,6 @@
         }
     }
 
-    /**
-     * Sets the emergency button visibility based on isEmergencyCallCapable().
-     *
-     * If the emergency button is visible, sets the text on the emergency button
-     * to indicate what action will be taken.
-     *
-     * If there's currently a call in progress, the button will take them to the call
-     * @param button The button to update
-     * @param shown Indicates whether the given screen wants the emergency button to show at all
-     * @param showIcon Indicates whether to show a phone icon for the button.
-     */
-    public void updateEmergencyCallButtonState(Button button, boolean shown, boolean showIcon) {
-        if (isEmergencyCallCapable() && shown) {
-            button.setVisibility(View.VISIBLE);
-        } else {
-            button.setVisibility(View.GONE);
-            return;
-        }
-
-        int textId;
-        if (isInCall()) {
-            // show "return to call" text and show phone icon
-            textId = R.string.lockscreen_return_to_call;
-            int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0;
-            button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
-        } else {
-            textId = R.string.lockscreen_emergency_call;
-            int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0;
-            button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
-        }
-        button.setText(textId);
-    }
-
-    /**
-     * Resumes a call in progress. Typically launched from the EmergencyCall button
-     * on various lockscreens.
-     */
-    public void resumeCall() {
-        getTelecommManager().showInCallScreen(false);
-    }
-
-    /**
-     * @return {@code true} if there is a call currently in progress, {@code false} otherwise.
-     */
-    public boolean isInCall() {
-        return getTelecommManager().isInCall();
-    }
-
-    private TelecomManager getTelecommManager() {
-        return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-    }
-
     public void setPowerButtonInstantlyLocks(boolean enabled) {
         setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled, getCurrentOrCallingUserId());
     }
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 4e48454..01e835b 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -593,15 +593,13 @@
     }
 
     @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setClassName(ResolverDrawerLayout.class.getName());
+    public CharSequence getAccessibilityClassName() {
+        return ResolverDrawerLayout.class.getName();
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(ResolverDrawerLayout.class.getName());
         if (isEnabled()) {
             if (mCollapseOffset != 0) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index a306697..825fcad 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -393,17 +393,9 @@
         }
 
         @Override
-        public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-            super.onInitializeAccessibilityEventInternal(event);
+        public CharSequence getAccessibilityClassName() {
             // This view masquerades as an action bar tab.
-            event.setClassName(ActionBar.Tab.class.getName());
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-            super.onInitializeAccessibilityNodeInfoInternal(info);
-            // This view masquerades as an action bar tab.
-            info.setClassName(ActionBar.Tab.class.getName());
+            return ActionBar.Tab.class.getName();
         }
 
         @Override
diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
deleted file mode 100644
index 5f3c5f9..0000000
--- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import java.lang.Math;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.StateSet;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.widget.RemoteViews.RemoteView;
-
-/**
- * A layout that switches between its children based on the requested layout height.
- * Each child specifies its minimum and maximum valid height.  Results are undefined
- * if children specify overlapping ranges.  A child may specify the maximum height
- * as 'unbounded' to indicate that it is willing to be displayed arbitrarily tall.
- *
- * <p>
- * See {@link SizeAdaptiveLayout.LayoutParams} for a full description of the
- * layout parameters used by SizeAdaptiveLayout.
- */
-@RemoteView
-public class SizeAdaptiveLayout extends ViewGroup {
-
-    private static final String TAG = "SizeAdaptiveLayout";
-    private static final boolean DEBUG = false;
-    private static final boolean REPORT_BAD_BOUNDS = true;
-    private static final long CROSSFADE_TIME = 250;
-
-    // TypedArray indices
-    private static final int MIN_VALID_HEIGHT =
-            R.styleable.SizeAdaptiveLayout_Layout_layout_minHeight;
-    private static final int MAX_VALID_HEIGHT =
-            R.styleable.SizeAdaptiveLayout_Layout_layout_maxHeight;
-
-    // view state
-    private View mActiveChild;
-    private View mLastActive;
-
-    // animation state
-    private AnimatorSet mTransitionAnimation;
-    private AnimatorListener mAnimatorListener;
-    private ObjectAnimator mFadePanel;
-    private ObjectAnimator mFadeView;
-    private int mCanceledAnimationCount;
-    private View mEnteringView;
-    private View mLeavingView;
-    // View used to hide larger views under smaller ones to create a uniform crossfade
-    private View mModestyPanel;
-    private int mModestyPanelTop;
-
-    public SizeAdaptiveLayout(Context context) {
-        this(context, null);
-    }
-
-    public SizeAdaptiveLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public SizeAdaptiveLayout(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        initialize();
-    }
-
-    private void initialize() {
-        mModestyPanel = new View(getContext());
-        // If the SizeAdaptiveLayout has a solid background, use it as a transition hint.
-        Drawable background = getBackground();
-        if (background instanceof StateListDrawable) {
-            StateListDrawable sld = (StateListDrawable) background;
-            sld.setState(StateSet.WILD_CARD);
-            background = sld.getCurrent();
-        }
-        if (background instanceof ColorDrawable) {
-            mModestyPanel.setBackgroundDrawable(background);
-        }
-        SizeAdaptiveLayout.LayoutParams layout =
-                new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                                                    ViewGroup.LayoutParams.MATCH_PARENT);
-        mModestyPanel.setLayoutParams(layout);
-        addView(mModestyPanel);
-        mFadePanel = ObjectAnimator.ofFloat(mModestyPanel, "alpha", 0f);
-        mFadeView = ObjectAnimator.ofFloat(null, "alpha", 0f);
-        mAnimatorListener = new BringToFrontOnEnd();
-        mTransitionAnimation = new AnimatorSet();
-        mTransitionAnimation.play(mFadeView).with(mFadePanel);
-        mTransitionAnimation.setDuration(CROSSFADE_TIME);
-        mTransitionAnimation.addListener(mAnimatorListener);
-    }
-
-    /**
-     * Visible for testing
-     * @hide
-     */
-    public Animator getTransitionAnimation() {
-        return mTransitionAnimation;
-    }
-
-    /**
-     * Visible for testing
-     * @hide
-     */
-    public View getModestyPanel() {
-        return mModestyPanel;
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        mLastActive = null;
-        // make sure all views start off invisible.
-        for (int i = 0; i < getChildCount(); i++) {
-            getChildAt(i).setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (DEBUG) Log.d(TAG, this + " measure spec: " +
-                         MeasureSpec.toString(heightMeasureSpec));
-        View model = selectActiveChild(heightMeasureSpec);
-        if (model == null) {
-            setMeasuredDimension(0, 0);
-            return;
-        }
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) model.getLayoutParams();
-        if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight);
-        measureChild(model, widthMeasureSpec, heightMeasureSpec);
-        int childHeight = model.getMeasuredHeight();
-        int childWidth = model.getMeasuredHeight();
-        int childState = combineMeasuredStates(0, model.getMeasuredState());
-        if (DEBUG) Log.d(TAG, "measured child at: " + childHeight);
-        int resolvedWidth = resolveSizeAndState(childWidth, widthMeasureSpec, childState);
-        int resolvedHeight = resolveSizeAndState(childHeight, heightMeasureSpec, childState);
-        if (DEBUG) Log.d(TAG, "resolved to: " + resolvedHeight);
-        int boundedHeight = clampSizeToBounds(resolvedHeight, model);
-        if (DEBUG) Log.d(TAG, "bounded to: " + boundedHeight);
-        setMeasuredDimension(resolvedWidth, boundedHeight);
-    }
-
-    private int clampSizeToBounds(int measuredHeight, View child) {
-        SizeAdaptiveLayout.LayoutParams lp =
-                (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
-        int heightIn = View.MEASURED_SIZE_MASK & measuredHeight;
-        int height = Math.max(heightIn, lp.minHeight);
-        if (lp.maxHeight != SizeAdaptiveLayout.LayoutParams.UNBOUNDED) {
-            height = Math.min(height, lp.maxHeight);
-        }
-
-        if (REPORT_BAD_BOUNDS && heightIn != height) {
-            Log.d(TAG, this + "child view " + child + " " +
-                  "measured out of bounds at " + heightIn +"px " +
-                  "clamped to " + height + "px");
-        }
-
-        return height;
-    }
-
-    //TODO extend to width and height
-    private View selectActiveChild(int heightMeasureSpec) {
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        View unboundedView = null;
-        View tallestView = null;
-        int tallestViewSize = 0;
-        View smallestView = null;
-        int smallestViewSize = Integer.MAX_VALUE;
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (child != mModestyPanel) {
-                SizeAdaptiveLayout.LayoutParams lp =
-                    (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
-                if (DEBUG) Log.d(TAG, "looking at " + i +
-                                 " with min: " + lp.minHeight +
-                                 " max: " +  lp.maxHeight);
-                if (lp.maxHeight == SizeAdaptiveLayout.LayoutParams.UNBOUNDED &&
-                    unboundedView == null) {
-                    unboundedView = child;
-                }
-                if (lp.maxHeight > tallestViewSize) {
-                    tallestViewSize = lp.maxHeight;
-                    tallestView = child;
-                }
-                if (lp.minHeight < smallestViewSize) {
-                    smallestViewSize = lp.minHeight;
-                    smallestView = child;
-                }
-                if (heightMode != MeasureSpec.UNSPECIFIED &&
-                    heightSize >= lp.minHeight && heightSize <= lp.maxHeight) {
-                    if (DEBUG) Log.d(TAG, "  found exact match, finishing early");
-                    return child;
-                }
-            }
-        }
-        if (unboundedView != null) {
-            tallestView = unboundedView;
-        }
-        if (heightMode == MeasureSpec.UNSPECIFIED || heightSize > tallestViewSize) {
-            return tallestView;
-        } else {
-            return smallestView;
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (DEBUG) Log.d(TAG, this + " onlayout height: " + (bottom - top));
-        mLastActive = mActiveChild;
-        int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top,
-                                                           View.MeasureSpec.EXACTLY);
-        mActiveChild = selectActiveChild(measureSpec);
-        if (mActiveChild == null) return;
-
-        mActiveChild.setVisibility(View.VISIBLE);
-
-        if (mLastActive != mActiveChild && mLastActive != null) {
-            if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive +
-                    " to: " + mActiveChild);
-
-            mEnteringView = mActiveChild;
-            mLeavingView = mLastActive;
-
-            mEnteringView.setAlpha(1f);
-
-            mModestyPanel.setAlpha(1f);
-            mModestyPanel.bringToFront();
-            mModestyPanelTop = mLeavingView.getHeight();
-            mModestyPanel.setVisibility(View.VISIBLE);
-            // TODO: mModestyPanel background should be compatible with mLeavingView
-
-            mLeavingView.bringToFront();
-
-            if (mTransitionAnimation.isRunning()) {
-                mTransitionAnimation.cancel();
-            }
-            mFadeView.setTarget(mLeavingView);
-            mFadeView.setFloatValues(0f);
-            mFadePanel.setFloatValues(0f);
-            mTransitionAnimation.setupStartValues();
-            mTransitionAnimation.start();
-        }
-        final int childWidth = mActiveChild.getMeasuredWidth();
-        final int childHeight = mActiveChild.getMeasuredHeight();
-        // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive
-        mActiveChild.layout(0, 0, childWidth, childHeight);
-
-        if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop);
-        mModestyPanel.layout(0, mModestyPanelTop, childWidth, mModestyPanelTop + childHeight);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        if (DEBUG) Log.d(TAG, "generate layout from attrs");
-        return new SizeAdaptiveLayout.LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        if (DEBUG) Log.d(TAG, "generate default layout from viewgroup");
-        return new SizeAdaptiveLayout.LayoutParams(p);
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        if (DEBUG) Log.d(TAG, "generate default layout from null");
-        return new SizeAdaptiveLayout.LayoutParams();
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof SizeAdaptiveLayout.LayoutParams;
-    }
-
-    /**
-     * Per-child layout information associated with ViewSizeAdaptiveLayout.
-     *
-     * TODO extend to width and height
-     *
-     * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_minHeight
-     * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_maxHeight
-     */
-    public static class LayoutParams extends ViewGroup.LayoutParams {
-
-        /**
-         * Indicates the minimum valid height for the child.
-         */
-        @ViewDebug.ExportedProperty(category = "layout")
-        public int minHeight;
-
-        /**
-         * Indicates the maximum valid height for the child.
-         */
-        @ViewDebug.ExportedProperty(category = "layout")
-        public int maxHeight;
-
-        /**
-         * Constant value for maxHeight that indicates there is not maximum height.
-         */
-        public static final int UNBOUNDED = -1;
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            if (DEBUG) {
-                Log.d(TAG, "construct layout from attrs");
-                for (int i = 0; i < attrs.getAttributeCount(); i++) {
-                    Log.d(TAG, " " + attrs.getAttributeName(i) + " = " +
-                          attrs.getAttributeValue(i));
-                }
-            }
-            TypedArray a =
-                    c.obtainStyledAttributes(attrs,
-                            R.styleable.SizeAdaptiveLayout_Layout);
-
-            minHeight = a.getDimensionPixelSize(MIN_VALID_HEIGHT, 0);
-            if (DEBUG) Log.d(TAG, "got minHeight of: " + minHeight);
-
-            try {
-                maxHeight = a.getLayoutDimension(MAX_VALID_HEIGHT, UNBOUNDED);
-                if (DEBUG) Log.d(TAG, "got maxHeight of: " + maxHeight);
-            } catch (Exception e) {
-                if (DEBUG) Log.d(TAG, "caught exception looking for maxValidHeight " + e);
-            }
-
-            a.recycle();
-        }
-
-        /**
-         * Creates a new set of layout parameters with the specified width, height
-         * and valid height bounds.
-         *
-         * @param width the width, either {@link #MATCH_PARENT},
-         *        {@link #WRAP_CONTENT} or a fixed size in pixels
-         * @param height the height, either {@link #MATCH_PARENT},
-         *        {@link #WRAP_CONTENT} or a fixed size in pixels
-         * @param minHeight the minimum height of this child
-         * @param maxHeight the maximum height of this child
-         *        or {@link #UNBOUNDED} if the child can grow forever
-         */
-        public LayoutParams(int width, int height, int minHeight, int maxHeight) {
-            super(width, height);
-            this.minHeight = minHeight;
-            this.maxHeight = maxHeight;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(int width, int height) {
-            this(width, height, UNBOUNDED, UNBOUNDED);
-        }
-
-        /**
-         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
-         */
-        public LayoutParams() {
-            this(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.LayoutParams p) {
-            super(p);
-            minHeight = UNBOUNDED;
-            maxHeight = UNBOUNDED;
-        }
-
-        public String debug(String output) {
-            return output + "SizeAdaptiveLayout.LayoutParams={" +
-                    ", max=" + maxHeight +
-                    ", max=" + minHeight + "}";
-        }
-    }
-
-    class BringToFrontOnEnd implements AnimatorListener {
-        @Override
-            public void onAnimationEnd(Animator animation) {
-            if (mCanceledAnimationCount == 0) {
-                mLeavingView.setVisibility(View.GONE);
-                mModestyPanel.setVisibility(View.GONE);
-                mEnteringView.bringToFront();
-                mEnteringView = null;
-                mLeavingView = null;
-            } else {
-                mCanceledAnimationCount--;
-            }
-        }
-
-        @Override
-            public void onAnimationCancel(Animator animation) {
-            mCanceledAnimationCount++;
-        }
-
-        @Override
-            public void onAnimationRepeat(Animator animation) {
-            if (DEBUG) Log.d(TAG, "fade animation repeated: should never happen.");
-            assert(false);
-        }
-
-        @Override
-            public void onAnimationStart(Animator animation) {
-        }
-    }
-}
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 2f987e9..a206e7f 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -148,6 +148,7 @@
         mHasMeasurements = false;
 
         requestLayout();
+        invalidate();
     }
 
     public void setForegroundColor(int color) {
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
deleted file mode 100644
index 9e7a649..0000000
--- a/core/java/com/android/internal/widget/WaveView.java
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import java.util.ArrayList;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.media.AudioAttributes;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-/**
- * A special widget containing a center and outer ring. Moving the center ring to the outer ring
- * causes an event that can be caught by implementing OnTriggerListener.
- */
-public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener {
-    private static final String TAG = "WaveView";
-    private static final boolean DBG = false;
-    private static final int WAVE_COUNT = 20; // default wave count
-    private static final long VIBRATE_SHORT = 20;  // msec
-    private static final long VIBRATE_LONG = 20;  // msec
-
-    // Lock state machine states
-    private static final int STATE_RESET_LOCK = 0;
-    private static final int STATE_READY = 1;
-    private static final int STATE_START_ATTEMPT = 2;
-    private static final int STATE_ATTEMPTING = 3;
-    private static final int STATE_UNLOCK_ATTEMPT = 4;
-    private static final int STATE_UNLOCK_SUCCESS = 5;
-
-    // Animation properties.
-    private static final long DURATION = 300; // duration of transitional animations
-    private static final long FINAL_DURATION = 200; // duration of final animations when unlocking
-    private static final long RING_DELAY = 1300; // when to start fading animated rings
-    private static final long FINAL_DELAY = 200; // delay for unlock success animation
-    private static final long SHORT_DELAY = 100; // for starting one animation after another.
-    private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay
-    private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
-    private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
-    private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
-    private static final long WAVE_DELAY = WAVE_DURATION / WAVE_COUNT; // initial propagation delay
-
-    /**
-     * The scale by which to multiply the unlock handle width to compute the radius
-     * in which it can be grabbed when accessibility is disabled.
-     */
-    private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED = 0.5f;
-
-    /**
-     * The scale by which to multiply the unlock handle width to compute the radius
-     * in which it can be grabbed when accessibility is enabled (more generous).
-     */
-    private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.0f;
-
-    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
-            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-            .build();
-
-    private Vibrator mVibrator;
-    private OnTriggerListener mOnTriggerListener;
-    private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3);
-    private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT);
-    private boolean mFingerDown = false;
-    private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
-    private int mSnapRadius = 136; // minimum threshold for drag unlock
-    private int mWaveCount = WAVE_COUNT;  // number of waves
-    private long mWaveTimerDelay = WAVE_DELAY;
-    private int mCurrentWave = 0;
-    private float mLockCenterX; // center of widget as dictated by widget size
-    private float mLockCenterY;
-    private float mMouseX; // current mouse position as of last touch event
-    private float mMouseY;
-    private DrawableHolder mUnlockRing;
-    private DrawableHolder mUnlockDefault;
-    private DrawableHolder mUnlockHalo;
-    private int mLockState = STATE_RESET_LOCK;
-    private int mGrabbedState = OnTriggerListener.NO_HANDLE;
-    private boolean mWavesRunning;
-    private boolean mFinishWaves;
-
-    public WaveView(Context context) {
-        this(context, null);
-    }
-
-    public WaveView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
-        // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL);
-        // a.recycle();
-
-        initDrawables();
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        mLockCenterX = 0.5f * w;
-        mLockCenterY = 0.5f * h;
-        super.onSizeChanged(w, h, oldw, oldh);
-    }
-
-    @Override
-    protected int getSuggestedMinimumWidth() {
-        // View should be large enough to contain the unlock ring + halo
-        return mUnlockRing.getWidth() + mUnlockHalo.getWidth();
-    }
-
-    @Override
-    protected int getSuggestedMinimumHeight() {
-        // View should be large enough to contain the unlock ring + halo
-        return mUnlockRing.getHeight() + mUnlockHalo.getHeight();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
-        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
-        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
-        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
-        int width;
-        int height;
-
-        if (widthSpecMode == MeasureSpec.AT_MOST) {
-            width = Math.min(widthSpecSize, getSuggestedMinimumWidth());
-        } else if (widthSpecMode == MeasureSpec.EXACTLY) {
-            width = widthSpecSize;
-        } else {
-            width = getSuggestedMinimumWidth();
-        }
-
-        if (heightSpecMode == MeasureSpec.AT_MOST) {
-            height = Math.min(heightSpecSize, getSuggestedMinimumWidth());
-        } else if (heightSpecMode == MeasureSpec.EXACTLY) {
-            height = heightSpecSize;
-        } else {
-            height = getSuggestedMinimumHeight();
-        }
-
-        setMeasuredDimension(width, height);
-    }
-
-    private void initDrawables() {
-        mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring));
-        mUnlockRing.setX(mLockCenterX);
-        mUnlockRing.setY(mLockCenterY);
-        mUnlockRing.setScaleX(0.1f);
-        mUnlockRing.setScaleY(0.1f);
-        mUnlockRing.setAlpha(0.0f);
-        mDrawables.add(mUnlockRing);
-
-        mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default));
-        mUnlockDefault.setX(mLockCenterX);
-        mUnlockDefault.setY(mLockCenterY);
-        mUnlockDefault.setScaleX(0.1f);
-        mUnlockDefault.setScaleY(0.1f);
-        mUnlockDefault.setAlpha(0.0f);
-        mDrawables.add(mUnlockDefault);
-
-        mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo));
-        mUnlockHalo.setX(mLockCenterX);
-        mUnlockHalo.setY(mLockCenterY);
-        mUnlockHalo.setScaleX(0.1f);
-        mUnlockHalo.setScaleY(0.1f);
-        mUnlockHalo.setAlpha(0.0f);
-        mDrawables.add(mUnlockHalo);
-
-        BitmapDrawable wave = createDrawable(R.drawable.unlock_wave);
-        for (int i = 0; i < mWaveCount; i++) {
-            DrawableHolder holder = new DrawableHolder(wave);
-            mLightWaves.add(holder);
-            holder.setAlpha(0.0f);
-        }
-    }
-
-    private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) {
-        double distX = mouseX - mLockCenterX;
-        double distY = mouseY - mLockCenterY;
-        int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
-        double touchA = Math.atan2(distX, distY);
-        float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA));
-        float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA));
-
-        switch (mLockState) {
-            case STATE_RESET_LOCK:
-                if (DBG) Log.v(TAG, "State RESET_LOCK");
-                mWaveTimerDelay = WAVE_DELAY;
-                for (int i = 0; i < mLightWaves.size(); i++) {
-                    DrawableHolder holder = mLightWaves.get(i);
-                    holder.addAnimTo(300, 0, "alpha", 0.0f, false);
-                }
-                for (int i = 0; i < mLightWaves.size(); i++) {
-                    mLightWaves.get(i).startAnimations(this);
-                }
-
-                mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true);
-                mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true);
-                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true);
-                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true);
-                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true);
-
-                mUnlockDefault.removeAnimationFor("x");
-                mUnlockDefault.removeAnimationFor("y");
-                mUnlockDefault.removeAnimationFor("scaleX");
-                mUnlockDefault.removeAnimationFor("scaleY");
-                mUnlockDefault.removeAnimationFor("alpha");
-                mUnlockDefault.setX(mLockCenterX);
-                mUnlockDefault.setY(mLockCenterY);
-                mUnlockDefault.setScaleX(0.1f);
-                mUnlockDefault.setScaleY(0.1f);
-                mUnlockDefault.setAlpha(0.0f);
-                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
-                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
-                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
-
-                mUnlockHalo.removeAnimationFor("x");
-                mUnlockHalo.removeAnimationFor("y");
-                mUnlockHalo.removeAnimationFor("scaleX");
-                mUnlockHalo.removeAnimationFor("scaleY");
-                mUnlockHalo.removeAnimationFor("alpha");
-                mUnlockHalo.setX(mLockCenterX);
-                mUnlockHalo.setY(mLockCenterY);
-                mUnlockHalo.setScaleX(0.1f);
-                mUnlockHalo.setScaleY(0.1f);
-                mUnlockHalo.setAlpha(0.0f);
-                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true);
-                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true);
-                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
-                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
-                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
-
-                removeCallbacks(mLockTimerActions);
-
-                mLockState = STATE_READY;
-                break;
-
-            case STATE_READY:
-                if (DBG) Log.v(TAG, "State READY");
-                mWaveTimerDelay = WAVE_DELAY;
-                break;
-
-            case STATE_START_ATTEMPT:
-                if (DBG) Log.v(TAG, "State START_ATTEMPT");
-                mUnlockDefault.removeAnimationFor("x");
-                mUnlockDefault.removeAnimationFor("y");
-                mUnlockDefault.removeAnimationFor("scaleX");
-                mUnlockDefault.removeAnimationFor("scaleY");
-                mUnlockDefault.removeAnimationFor("alpha");
-                mUnlockDefault.setX(mLockCenterX + 182);
-                mUnlockDefault.setY(mLockCenterY);
-                mUnlockDefault.setScaleX(0.1f);
-                mUnlockDefault.setScaleY(0.1f);
-                mUnlockDefault.setAlpha(0.0f);
-
-                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false);
-                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false);
-                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false);
-
-                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
-                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
-                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
-
-                mLockState = STATE_ATTEMPTING;
-                break;
-
-            case STATE_ATTEMPTING:
-                if (DBG) Log.v(TAG, "State ATTEMPTING (fingerDown = " + fingerDown + ")");
-                if (dragDistance > mSnapRadius) {
-                    mFinishWaves = true; // don't start any more waves.
-                    if (fingerDown) {
-                        mUnlockHalo.addAnimTo(0, 0, "x", ringX, true);
-                        mUnlockHalo.addAnimTo(0, 0, "y", ringY, true);
-                        mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
-                        mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
-                        mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
-                    }  else {
-                        if (DBG) Log.v(TAG, "up detected, moving to STATE_UNLOCK_ATTEMPT");
-                        mLockState = STATE_UNLOCK_ATTEMPT;
-                    }
-                } else {
-                    // If waves have stopped, we need to kick them off again...
-                    if (!mWavesRunning) {
-                        mWavesRunning = true;
-                        mFinishWaves = false;
-                        // mWaveTimerDelay = WAVE_DELAY;
-                        postDelayed(mAddWaveAction, mWaveTimerDelay);
-                    }
-                    mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true);
-                    mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true);
-                    mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
-                    mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
-                    mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
-                }
-                break;
-
-            case STATE_UNLOCK_ATTEMPT:
-                if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT");
-                if (dragDistance > mSnapRadius) {
-                    for (int n = 0; n < mLightWaves.size(); n++) {
-                        DrawableHolder wave = mLightWaves.get(n);
-                        long delay = 1000L*(6 + n - mCurrentWave)/10L;
-                        wave.addAnimTo(FINAL_DURATION, delay, "x", ringX, true);
-                        wave.addAnimTo(FINAL_DURATION, delay, "y", ringY, true);
-                        wave.addAnimTo(FINAL_DURATION, delay, "scaleX", 0.1f, true);
-                        wave.addAnimTo(FINAL_DURATION, delay, "scaleY", 0.1f, true);
-                        wave.addAnimTo(FINAL_DURATION, delay, "alpha", 0.0f, true);
-                    }
-                    for (int i = 0; i < mLightWaves.size(); i++) {
-                        mLightWaves.get(i).startAnimations(this);
-                    }
-
-                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
-                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
-                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleX", 0.1f, false);
-                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleY", 0.1f, false);
-                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "alpha", 0.0f, false);
-
-                    mUnlockRing.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
-                    mUnlockDefault.removeAnimationFor("x");
-                    mUnlockDefault.removeAnimationFor("y");
-                    mUnlockDefault.removeAnimationFor("scaleX");
-                    mUnlockDefault.removeAnimationFor("scaleY");
-                    mUnlockDefault.removeAnimationFor("alpha");
-                    mUnlockDefault.setX(ringX);
-                    mUnlockDefault.setY(ringY);
-                    mUnlockDefault.setScaleX(0.1f);
-                    mUnlockDefault.setScaleY(0.1f);
-                    mUnlockDefault.setAlpha(0.0f);
-
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "x", ringX, true);
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "y", ringY, true);
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleX", 1.0f, true);
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleY", 1.0f, true);
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "alpha", 1.0f, true);
-
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
-                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
-                    mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
-                    mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
-
-                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
-                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
-                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
-                    removeCallbacks(mLockTimerActions);
-
-                    postDelayed(mLockTimerActions, RESET_TIMEOUT);
-
-                    dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE);
-                    mLockState = STATE_UNLOCK_SUCCESS;
-                } else {
-                    mLockState = STATE_RESET_LOCK;
-                }
-                break;
-
-            case STATE_UNLOCK_SUCCESS:
-                if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS");
-                removeCallbacks(mAddWaveAction);
-                break;
-
-            default:
-                if (DBG) Log.v(TAG, "Unknown state " + mLockState);
-                break;
-        }
-        mUnlockDefault.startAnimations(this);
-        mUnlockHalo.startAnimations(this);
-        mUnlockRing.startAnimations(this);
-    }
-
-    BitmapDrawable createDrawable(int resId) {
-        Resources res = getResources();
-        Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
-        return new BitmapDrawable(res, bitmap);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
-        for (int i = 0; i < mDrawables.size(); ++i) {
-            mDrawables.get(i).draw(canvas);
-        }
-        for (int i = 0; i < mLightWaves.size(); ++i) {
-            mLightWaves.get(i).draw(canvas);
-        }
-    }
-
-    private final Runnable mLockTimerActions = new Runnable() {
-        public void run() {
-            if (DBG) Log.v(TAG, "LockTimerActions");
-            // reset lock after inactivity
-            if (mLockState == STATE_ATTEMPTING) {
-                if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK");
-                mLockState = STATE_RESET_LOCK;
-            }
-            // for prototype, reset after successful unlock
-            if (mLockState == STATE_UNLOCK_SUCCESS) {
-                if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK after success");
-                mLockState = STATE_RESET_LOCK;
-            }
-            invalidate();
-        }
-    };
-
-    private final Runnable mAddWaveAction = new Runnable() {
-        public void run() {
-            double distX = mMouseX - mLockCenterX;
-            double distY = mMouseY - mLockCenterY;
-            int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
-            if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
-                    && mWaveTimerDelay >= WAVE_DELAY) {
-                mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
-
-                DrawableHolder wave = mLightWaves.get(mCurrentWave);
-                wave.setAlpha(0.0f);
-                wave.setScaleX(0.2f);
-                wave.setScaleY(0.2f);
-                wave.setX(mMouseX);
-                wave.setY(mMouseY);
-
-                wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true);
-                wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true);
-                wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true);
-                wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true);
-                wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true);
-
-                wave.addAnimTo(1000, RING_DELAY, "alpha", 0.0f, false);
-                wave.startAnimations(WaveView.this);
-
-                mCurrentWave = (mCurrentWave+1) % mWaveCount;
-                if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay);
-            } else {
-                mWaveTimerDelay += DELAY_INCREMENT2;
-            }
-            if (mFinishWaves) {
-                // sentinel used to restart the waves after they've stopped
-                mWavesRunning = false;
-            } else {
-                postDelayed(mAddWaveAction, mWaveTimerDelay);
-            }
-        }
-    };
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
-            final int action = event.getAction();
-            switch (action) {
-                case MotionEvent.ACTION_HOVER_ENTER:
-                    event.setAction(MotionEvent.ACTION_DOWN);
-                    break;
-                case MotionEvent.ACTION_HOVER_MOVE:
-                    event.setAction(MotionEvent.ACTION_MOVE);
-                    break;
-                case MotionEvent.ACTION_HOVER_EXIT:
-                    event.setAction(MotionEvent.ACTION_UP);
-                    break;
-            }
-            onTouchEvent(event);
-            event.setAction(action);
-        }
-        return super.onHoverEvent(event);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        final int action = event.getAction();
-        mMouseX = event.getX();
-        mMouseY = event.getY();
-        boolean handled = false;
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                removeCallbacks(mLockTimerActions);
-                mFingerDown = true;
-                tryTransitionToStartAttemptState(event);
-                handled = true;
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                tryTransitionToStartAttemptState(event);
-                handled = true;
-                break;
-
-            case MotionEvent.ACTION_UP:
-                if (DBG) Log.v(TAG, "ACTION_UP");
-                mFingerDown = false;
-                postDelayed(mLockTimerActions, RESET_TIMEOUT);
-                setGrabbedState(OnTriggerListener.NO_HANDLE);
-                // Normally the state machine is driven by user interaction causing redraws.
-                // However, when there's no more user interaction and no running animations,
-                // the state machine stops advancing because onDraw() never gets called.
-                // The following ensures we advance to the next state in this case,
-                // either STATE_UNLOCK_ATTEMPT or STATE_RESET_LOCK.
-                waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
-                handled = true;
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-                mFingerDown = false;
-                handled = true;
-                break;
-        }
-        invalidate();
-        return handled ? true : super.onTouchEvent(event);
-    }
-
-    /**
-     * Tries to transition to start attempt state.
-     *
-     * @param event A motion event.
-     */
-    private void tryTransitionToStartAttemptState(MotionEvent event) {
-        final float dx = event.getX() - mUnlockHalo.getX();
-        final float dy = event.getY() - mUnlockHalo.getY();
-        float dist = (float) Math.hypot(dx, dy);
-        if (dist <= getScaledGrabHandleRadius()) {
-            setGrabbedState(OnTriggerListener.CENTER_HANDLE);
-            if (mLockState == STATE_READY) {
-                mLockState = STATE_START_ATTEMPT;
-                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                    announceUnlockHandle();
-                }
-            }
-        }
-    }
-
-    /**
-     * @return The radius in which the handle is grabbed scaled based on
-     *     whether accessibility is enabled.
-     */
-    private float getScaledGrabHandleRadius() {
-        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-            return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mUnlockHalo.getWidth();
-        } else {
-            return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED * mUnlockHalo.getWidth();
-        }
-    }
-
-    /**
-     * Announces the unlock handle if accessibility is enabled.
-     */
-    private void announceUnlockHandle() {
-        setContentDescription(mContext.getString(R.string.description_target_unlock_tablet));
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-        setContentDescription(null);
-    }
-
-    /**
-     * Triggers haptic feedback.
-     */
-    private synchronized void vibrate(long duration) {
-        final boolean hapticEnabled = Settings.System.getIntForUser(
-                mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
-                UserHandle.USER_CURRENT) != 0;
-        if (hapticEnabled) {
-            if (mVibrator == null) {
-                mVibrator = (android.os.Vibrator) getContext()
-                        .getSystemService(Context.VIBRATOR_SERVICE);
-            }
-            mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES);
-        }
-    }
-
-    /**
-     * Registers a callback to be invoked when the user triggers an event.
-     *
-     * @param listener the OnDialTriggerListener to attach to this view
-     */
-    public void setOnTriggerListener(OnTriggerListener listener) {
-        mOnTriggerListener = listener;
-    }
-
-    /**
-     * Dispatches a trigger event to listener. Ignored if a listener is not set.
-     * @param whichHandle the handle that triggered the event.
-     */
-    private void dispatchTriggerEvent(int whichHandle) {
-        vibrate(VIBRATE_LONG);
-        if (mOnTriggerListener != null) {
-            mOnTriggerListener.onTrigger(this, whichHandle);
-        }
-    }
-
-    /**
-     * Sets the current grabbed state, and dispatches a grabbed state change
-     * event to our listener.
-     */
-    private void setGrabbedState(int newState) {
-        if (newState != mGrabbedState) {
-            mGrabbedState = newState;
-            if (mOnTriggerListener != null) {
-                mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
-            }
-        }
-    }
-
-    public interface OnTriggerListener {
-        /**
-         * Sent when the user releases the handle.
-         */
-        public static final int NO_HANDLE = 0;
-
-        /**
-         * Sent when the user grabs the center handle
-         */
-        public static final int CENTER_HANDLE = 10;
-
-        /**
-         * Called when the user drags the center ring beyond a threshold.
-         */
-        void onTrigger(View v, int whichHandle);
-
-        /**
-         * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
-         * one of the handles.)
-         *
-         * @param v the view that was triggered
-         * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE},
-         */
-        void onGrabbedStateChange(View v, int grabbedState);
-    }
-
-    public void onAnimationUpdate(ValueAnimator animation) {
-        invalidate();
-    }
-
-    public void reset() {
-        if (DBG) Log.v(TAG, "reset() : resets state to STATE_RESET_LOCK");
-        mLockState = STATE_RESET_LOCK;
-        invalidate();
-    }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/Ease.java b/core/java/com/android/internal/widget/multiwaveview/Ease.java
deleted file mode 100644
index 7f90c44..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/Ease.java
+++ /dev/null
@@ -1,132 +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.internal.widget.multiwaveview;
-
-import android.animation.TimeInterpolator;
-
-class Ease {
-    private static final float DOMAIN = 1.0f;
-    private static final float DURATION = 1.0f;
-    private static final float START = 0.0f;
-
-    static class Linear {
-        public static final TimeInterpolator easeNone = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return input;
-            }
-        };
-    }
-
-    static class Cubic {
-        public static final TimeInterpolator easeIn = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return DOMAIN*(input/=DURATION)*input*input + START;
-            }
-        };
-        public static final TimeInterpolator easeOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return DOMAIN*((input=input/DURATION-1)*input*input + 1) + START;
-            }
-        };
-        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return ((input/=DURATION/2) < 1.0f) ?
-                        (DOMAIN/2*input*input*input + START)
-                            : (DOMAIN/2*((input-=2)*input*input + 2) + START);
-            }
-        };
-    }
-
-    static class Quad {
-        public static final TimeInterpolator easeIn = new TimeInterpolator() {
-            public float getInterpolation (float input) {
-                return DOMAIN*(input/=DURATION)*input + START;
-            }
-        };
-        public static final TimeInterpolator easeOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return -DOMAIN *(input/=DURATION)*(input-2) + START;
-            }
-        };
-        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return ((input/=DURATION/2) < 1) ?
-                        (DOMAIN/2*input*input + START)
-                            : (-DOMAIN/2 * ((--input)*(input-2) - 1) + START);
-            }
-        };
-    }
-
-    static class Quart {
-        public static final TimeInterpolator easeIn = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return DOMAIN*(input/=DURATION)*input*input*input + START;
-            }
-        };
-        public static final TimeInterpolator easeOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return -DOMAIN * ((input=input/DURATION-1)*input*input*input - 1) + START;
-            }
-        };
-        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return ((input/=DURATION/2) < 1) ?
-                        (DOMAIN/2*input*input*input*input + START)
-                            : (-DOMAIN/2 * ((input-=2)*input*input*input - 2) + START);
-            }
-        };
-    }
-
-    static class Quint {
-        public static final TimeInterpolator easeIn = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return DOMAIN*(input/=DURATION)*input*input*input*input + START;
-            }
-        };
-        public static final TimeInterpolator easeOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return DOMAIN*((input=input/DURATION-1)*input*input*input*input + 1) + START;
-            }
-        };
-        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return ((input/=DURATION/2) < 1) ?
-                        (DOMAIN/2*input*input*input*input*input + START)
-                            : (DOMAIN/2*((input-=2)*input*input*input*input + 2) + START);
-            }
-        };
-    }
-
-    static class Sine {
-        public static final TimeInterpolator easeIn = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return -DOMAIN * (float) Math.cos(input/DURATION * (Math.PI/2)) + DOMAIN + START;
-            }
-        };
-        public static final TimeInterpolator easeOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return DOMAIN * (float) Math.sin(input/DURATION * (Math.PI/2)) + START;
-            }
-        };
-        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
-            public float getInterpolation(float input) {
-                return -DOMAIN/2 * ((float)Math.cos(Math.PI*input/DURATION) - 1.0f) + START;
-            }
-        };
-    }
-
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
deleted file mode 100644
index 11ac19e..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget.multiwaveview;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-
-/**
- * A re-usable widget containing a center, outer ring and wave animation.
- */
-public class GlowPadView extends View {
-    private static final String TAG = "GlowPadView";
-    private static final boolean DEBUG = false;
-
-    // Wave state machine
-    private static final int STATE_IDLE = 0;
-    private static final int STATE_START = 1;
-    private static final int STATE_FIRST_TOUCH = 2;
-    private static final int STATE_TRACKING = 3;
-    private static final int STATE_SNAP = 4;
-    private static final int STATE_FINISH = 5;
-
-    // Animation properties.
-    private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
-
-    public interface OnTriggerListener {
-        int NO_HANDLE = 0;
-        int CENTER_HANDLE = 1;
-        public void onGrabbed(View v, int handle);
-        public void onReleased(View v, int handle);
-        public void onTrigger(View v, int target);
-        public void onGrabbedStateChange(View v, int handle);
-        public void onFinishFinalAnimation();
-    }
-
-    // Tuneable parameters for animation
-    private static final int WAVE_ANIMATION_DURATION = 1000;
-    private static final int RETURN_TO_HOME_DELAY = 1200;
-    private static final int RETURN_TO_HOME_DURATION = 200;
-    private static final int HIDE_ANIMATION_DELAY = 200;
-    private static final int HIDE_ANIMATION_DURATION = 200;
-    private static final int SHOW_ANIMATION_DURATION = 200;
-    private static final int SHOW_ANIMATION_DELAY = 50;
-    private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
-    private static final int REVEAL_GLOW_DELAY = 0;
-    private static final int REVEAL_GLOW_DURATION = 0;
-
-    private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
-    private static final float TARGET_SCALE_EXPANDED = 1.0f;
-    private static final float TARGET_SCALE_COLLAPSED = 0.8f;
-    private static final float RING_SCALE_EXPANDED = 1.0f;
-    private static final float RING_SCALE_COLLAPSED = 0.5f;
-
-    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
-            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-            .build();
-
-    private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
-    private AnimationBundle mWaveAnimations = new AnimationBundle();
-    private AnimationBundle mTargetAnimations = new AnimationBundle();
-    private AnimationBundle mGlowAnimations = new AnimationBundle();
-    private ArrayList<String> mTargetDescriptions;
-    private ArrayList<String> mDirectionDescriptions;
-    private OnTriggerListener mOnTriggerListener;
-    private TargetDrawable mHandleDrawable;
-    private TargetDrawable mOuterRing;
-    private Vibrator mVibrator;
-
-    private int mFeedbackCount = 3;
-    private int mVibrationDuration = 0;
-    private int mGrabbedState;
-    private int mActiveTarget = -1;
-    private float mGlowRadius;
-    private float mWaveCenterX;
-    private float mWaveCenterY;
-    private int mMaxTargetHeight;
-    private int mMaxTargetWidth;
-    private float mRingScaleFactor = 1f;
-    private boolean mAllowScaling;
-
-    private float mOuterRadius = 0.0f;
-    private float mSnapMargin = 0.0f;
-    private float mFirstItemOffset = 0.0f;
-    private boolean mMagneticTargets = false;
-    private boolean mDragging;
-    private int mNewTargetResources;
-
-    private class AnimationBundle extends ArrayList<Tweener> {
-        private static final long serialVersionUID = 0xA84D78726F127468L;
-        private boolean mSuspended;
-
-        public void start() {
-            if (mSuspended) return; // ignore attempts to start animations
-            final int count = size();
-            for (int i = 0; i < count; i++) {
-                Tweener anim = get(i);
-                anim.animator.start();
-            }
-        }
-
-        public void cancel() {
-            final int count = size();
-            for (int i = 0; i < count; i++) {
-                Tweener anim = get(i);
-                anim.animator.cancel();
-            }
-            clear();
-        }
-
-        public void stop() {
-            final int count = size();
-            for (int i = 0; i < count; i++) {
-                Tweener anim = get(i);
-                anim.animator.end();
-            }
-            clear();
-        }
-
-        public void setSuspended(boolean suspend) {
-            mSuspended = suspend;
-        }
-    };
-
-    private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
-        public void onAnimationEnd(Animator animator) {
-            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
-            dispatchOnFinishFinalAnimation();
-        }
-    };
-
-    private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
-        public void onAnimationEnd(Animator animator) {
-            ping();
-            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
-            dispatchOnFinishFinalAnimation();
-        }
-    };
-
-    private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
-        public void onAnimationUpdate(ValueAnimator animation) {
-            invalidate();
-        }
-    };
-
-    private boolean mAnimatingTargets;
-    private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
-        public void onAnimationEnd(Animator animator) {
-            if (mNewTargetResources != 0) {
-                internalSetTargetResources(mNewTargetResources);
-                mNewTargetResources = 0;
-                hideTargets(false, false);
-            }
-            mAnimatingTargets = false;
-        }
-    };
-    private int mTargetResourceId;
-    private int mTargetDescriptionsResourceId;
-    private int mDirectionDescriptionsResourceId;
-    private boolean mAlwaysTrackFinger;
-    private int mHorizontalInset;
-    private int mVerticalInset;
-    private int mGravity = Gravity.TOP;
-    private boolean mInitialLayout = true;
-    private Tweener mBackgroundAnimator;
-    private PointCloud mPointCloud;
-    private float mInnerRadius;
-    private int mPointerId;
-
-    public GlowPadView(Context context) {
-        this(context, null);
-    }
-
-    public GlowPadView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        Resources res = context.getResources();
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView);
-        mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
-        mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
-        mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
-        mFirstItemOffset = (float) Math.toRadians(
-                a.getFloat(R.styleable.GlowPadView_firstItemOffset,
-                        (float) Math.toDegrees(mFirstItemOffset)));
-        mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
-                mVibrationDuration);
-        mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
-                mFeedbackCount);
-        mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
-        TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
-        mHandleDrawable = new TargetDrawable(res, handle != null ? handle.resourceId : 0);
-        mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
-        mOuterRing = new TargetDrawable(res,
-                getResourceId(a, R.styleable.GlowPadView_outerRingDrawable));
-
-        mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
-        mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets);
-
-        int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
-        Drawable pointDrawable = pointId != 0 ? context.getDrawable(pointId) : null;
-        mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
-
-        mPointCloud = new PointCloud(pointDrawable);
-        mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
-        mPointCloud.glowManager.setRadius(mGlowRadius);
-
-        TypedValue outValue = new TypedValue();
-
-        // Read array of target drawables
-        if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) {
-            internalSetTargetResources(outValue.resourceId);
-        }
-        if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
-            throw new IllegalStateException("Must specify at least one target drawable");
-        }
-
-        // Read array of target descriptions
-        if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) {
-            final int resourceId = outValue.resourceId;
-            if (resourceId == 0) {
-                throw new IllegalStateException("Must specify target descriptions");
-            }
-            setTargetDescriptionsResourceId(resourceId);
-        }
-
-        // Read array of direction descriptions
-        if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) {
-            final int resourceId = outValue.resourceId;
-            if (resourceId == 0) {
-                throw new IllegalStateException("Must specify direction descriptions");
-            }
-            setDirectionDescriptionsResourceId(resourceId);
-        }
-
-        mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
-
-        a.recycle();
-
-        setVibrateEnabled(mVibrationDuration > 0);
-
-        assignDefaultsIfNeeded();
-    }
-
-    private int getResourceId(TypedArray a, int id) {
-        TypedValue tv = a.peekValue(id);
-        return tv == null ? 0 : tv.resourceId;
-    }
-
-    private void dump() {
-        Log.v(TAG, "Outer Radius = " + mOuterRadius);
-        Log.v(TAG, "SnapMargin = " + mSnapMargin);
-        Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
-        Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
-        Log.v(TAG, "GlowRadius = " + mGlowRadius);
-        Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
-        Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
-    }
-
-    public void suspendAnimations() {
-        mWaveAnimations.setSuspended(true);
-        mTargetAnimations.setSuspended(true);
-        mGlowAnimations.setSuspended(true);
-    }
-
-    public void resumeAnimations() {
-        mWaveAnimations.setSuspended(false);
-        mTargetAnimations.setSuspended(false);
-        mGlowAnimations.setSuspended(false);
-        mWaveAnimations.start();
-        mTargetAnimations.start();
-        mGlowAnimations.start();
-    }
-
-    @Override
-    protected int getSuggestedMinimumWidth() {
-        // View should be large enough to contain the background + handle and
-        // target drawable on either edge.
-        return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
-    }
-
-    @Override
-    protected int getSuggestedMinimumHeight() {
-        // View should be large enough to contain the unlock ring + target and
-        // target drawable on either edge
-        return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
-    }
-
-    /**
-     * This gets the suggested width accounting for the ring's scale factor.
-     */
-    protected int getScaledSuggestedMinimumWidth() {
-        return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
-                + mMaxTargetWidth);
-    }
-
-    /**
-     * This gets the suggested height accounting for the ring's scale factor.
-     */
-    protected int getScaledSuggestedMinimumHeight() {
-        return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
-                + mMaxTargetHeight);
-    }
-
-    private int resolveMeasured(int measureSpec, int desired)
-    {
-        int result = 0;
-        int specSize = MeasureSpec.getSize(measureSpec);
-        switch (MeasureSpec.getMode(measureSpec)) {
-            case MeasureSpec.UNSPECIFIED:
-                result = desired;
-                break;
-            case MeasureSpec.AT_MOST:
-                result = Math.min(specSize, desired);
-                break;
-            case MeasureSpec.EXACTLY:
-            default:
-                result = specSize;
-        }
-        return result;
-    }
-
-    private void switchToState(int state, float x, float y) {
-        switch (state) {
-            case STATE_IDLE:
-                deactivateTargets();
-                hideGlow(0, 0, 0.0f, null);
-                startBackgroundAnimation(0, 0.0f);
-                mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
-                mHandleDrawable.setAlpha(1.0f);
-                break;
-
-            case STATE_START:
-                startBackgroundAnimation(0, 0.0f);
-                break;
-
-            case STATE_FIRST_TOUCH:
-                mHandleDrawable.setAlpha(0.0f);
-                deactivateTargets();
-                showTargets(true);
-                startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
-                setGrabbedState(OnTriggerListener.CENTER_HANDLE);
-                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                    announceTargets();
-                }
-                break;
-
-            case STATE_TRACKING:
-                mHandleDrawable.setAlpha(0.0f);
-                showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null);
-                break;
-
-            case STATE_SNAP:
-                // TODO: Add transition states (see list_selector_background_transition.xml)
-                mHandleDrawable.setAlpha(0.0f);
-                showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null);
-                break;
-
-            case STATE_FINISH:
-                doFinish();
-                break;
-        }
-    }
-
-    private void showGlow(int duration, int delay, float finalAlpha,
-            AnimatorListener finishListener) {
-        mGlowAnimations.cancel();
-        mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
-                "ease", Ease.Cubic.easeIn,
-                "delay", delay,
-                "alpha", finalAlpha,
-                "onUpdate", mUpdateListener,
-                "onComplete", finishListener));
-        mGlowAnimations.start();
-    }
-
-    private void hideGlow(int duration, int delay, float finalAlpha,
-            AnimatorListener finishListener) {
-        mGlowAnimations.cancel();
-        mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
-                "ease", Ease.Quart.easeOut,
-                "delay", delay,
-                "alpha", finalAlpha,
-                "x", 0.0f,
-                "y", 0.0f,
-                "onUpdate", mUpdateListener,
-                "onComplete", finishListener));
-        mGlowAnimations.start();
-    }
-
-    private void deactivateTargets() {
-        final int count = mTargetDrawables.size();
-        for (int i = 0; i < count; i++) {
-            TargetDrawable target = mTargetDrawables.get(i);
-            target.setState(TargetDrawable.STATE_INACTIVE);
-        }
-        mActiveTarget = -1;
-    }
-
-    /**
-     * Dispatches a trigger event to listener. Ignored if a listener is not set.
-     * @param whichTarget the target that was triggered.
-     */
-    private void dispatchTriggerEvent(int whichTarget) {
-        vibrate();
-        if (mOnTriggerListener != null) {
-            mOnTriggerListener.onTrigger(this, whichTarget);
-        }
-    }
-
-    private void dispatchOnFinishFinalAnimation() {
-        if (mOnTriggerListener != null) {
-            mOnTriggerListener.onFinishFinalAnimation();
-        }
-    }
-
-    private void doFinish() {
-        final int activeTarget = mActiveTarget;
-        final boolean targetHit =  activeTarget != -1;
-
-        if (targetHit) {
-            if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
-
-            highlightSelected(activeTarget);
-
-            // Inform listener of any active targets.  Typically only one will be active.
-            hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
-            dispatchTriggerEvent(activeTarget);
-            if (!mAlwaysTrackFinger) {
-                // Force ring and targets to finish animation to final expanded state
-                mTargetAnimations.stop();
-            }
-        } else {
-            // Animate handle back to the center based on current state.
-            hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing);
-            hideTargets(true, false);
-        }
-
-        setGrabbedState(OnTriggerListener.NO_HANDLE);
-    }
-
-    private void highlightSelected(int activeTarget) {
-        // Highlight the given target and fade others
-        mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
-        hideUnselected(activeTarget);
-    }
-
-    private void hideUnselected(int active) {
-        for (int i = 0; i < mTargetDrawables.size(); i++) {
-            if (i != active) {
-                mTargetDrawables.get(i).setAlpha(0.0f);
-            }
-        }
-    }
-
-    private void hideTargets(boolean animate, boolean expanded) {
-        mTargetAnimations.cancel();
-        // Note: these animations should complete at the same time so that we can swap out
-        // the target assets asynchronously from the setTargetResources() call.
-        mAnimatingTargets = animate;
-        final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
-        final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
-
-        final float targetScale = expanded ?
-                TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
-        final int length = mTargetDrawables.size();
-        final TimeInterpolator interpolator = Ease.Cubic.easeOut;
-        for (int i = 0; i < length; i++) {
-            TargetDrawable target = mTargetDrawables.get(i);
-            target.setState(TargetDrawable.STATE_INACTIVE);
-            mTargetAnimations.add(Tweener.to(target, duration,
-                    "ease", interpolator,
-                    "alpha", 0.0f,
-                    "scaleX", targetScale,
-                    "scaleY", targetScale,
-                    "delay", delay,
-                    "onUpdate", mUpdateListener));
-        }
-
-        float ringScaleTarget = expanded ?
-                RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
-        ringScaleTarget *= mRingScaleFactor;
-        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
-                "ease", interpolator,
-                "alpha", 0.0f,
-                "scaleX", ringScaleTarget,
-                "scaleY", ringScaleTarget,
-                "delay", delay,
-                "onUpdate", mUpdateListener,
-                "onComplete", mTargetUpdateListener));
-
-        mTargetAnimations.start();
-    }
-
-    private void showTargets(boolean animate) {
-        mTargetAnimations.stop();
-        mAnimatingTargets = animate;
-        final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
-        final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
-        final int length = mTargetDrawables.size();
-        for (int i = 0; i < length; i++) {
-            TargetDrawable target = mTargetDrawables.get(i);
-            target.setState(TargetDrawable.STATE_INACTIVE);
-            mTargetAnimations.add(Tweener.to(target, duration,
-                    "ease", Ease.Cubic.easeOut,
-                    "alpha", 1.0f,
-                    "scaleX", 1.0f,
-                    "scaleY", 1.0f,
-                    "delay", delay,
-                    "onUpdate", mUpdateListener));
-        }
-
-        float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
-        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
-                "ease", Ease.Cubic.easeOut,
-                "alpha", 1.0f,
-                "scaleX", ringScale,
-                "scaleY", ringScale,
-                "delay", delay,
-                "onUpdate", mUpdateListener,
-                "onComplete", mTargetUpdateListener));
-
-        mTargetAnimations.start();
-    }
-
-    private void vibrate() {
-        final boolean hapticEnabled = Settings.System.getIntForUser(
-                mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
-                UserHandle.USER_CURRENT) != 0;
-        if (mVibrator != null && hapticEnabled) {
-            mVibrator.vibrate(mVibrationDuration, VIBRATION_ATTRIBUTES);
-        }
-    }
-
-    private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
-        Resources res = getContext().getResources();
-        TypedArray array = res.obtainTypedArray(resourceId);
-        final int count = array.length();
-        ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
-        for (int i = 0; i < count; i++) {
-            TypedValue value = array.peekValue(i);
-            TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0);
-            drawables.add(target);
-        }
-        array.recycle();
-        return drawables;
-    }
-
-    private void internalSetTargetResources(int resourceId) {
-        final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId);
-        mTargetDrawables = targets;
-        mTargetResourceId = resourceId;
-
-        int maxWidth = mHandleDrawable.getWidth();
-        int maxHeight = mHandleDrawable.getHeight();
-        final int count = targets.size();
-        for (int i = 0; i < count; i++) {
-            TargetDrawable target = targets.get(i);
-            maxWidth = Math.max(maxWidth, target.getWidth());
-            maxHeight = Math.max(maxHeight, target.getHeight());
-        }
-        if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
-            mMaxTargetWidth = maxWidth;
-            mMaxTargetHeight = maxHeight;
-            requestLayout(); // required to resize layout and call updateTargetPositions()
-        } else {
-            updateTargetPositions(mWaveCenterX, mWaveCenterY);
-            updatePointCloudPosition(mWaveCenterX, mWaveCenterY);
-        }
-    }
-
-    /**
-     * Loads an array of drawables from the given resourceId.
-     *
-     * @param resourceId
-     */
-    public void setTargetResources(int resourceId) {
-        if (mAnimatingTargets) {
-            // postpone this change until we return to the initial state
-            mNewTargetResources = resourceId;
-        } else {
-            internalSetTargetResources(resourceId);
-        }
-    }
-
-    public int getTargetResourceId() {
-        return mTargetResourceId;
-    }
-
-    /**
-     * Sets the resource id specifying the target descriptions for accessibility.
-     *
-     * @param resourceId The resource id.
-     */
-    public void setTargetDescriptionsResourceId(int resourceId) {
-        mTargetDescriptionsResourceId = resourceId;
-        if (mTargetDescriptions != null) {
-            mTargetDescriptions.clear();
-        }
-    }
-
-    /**
-     * Gets the resource id specifying the target descriptions for accessibility.
-     *
-     * @return The resource id.
-     */
-    public int getTargetDescriptionsResourceId() {
-        return mTargetDescriptionsResourceId;
-    }
-
-    /**
-     * Sets the resource id specifying the target direction descriptions for accessibility.
-     *
-     * @param resourceId The resource id.
-     */
-    public void setDirectionDescriptionsResourceId(int resourceId) {
-        mDirectionDescriptionsResourceId = resourceId;
-        if (mDirectionDescriptions != null) {
-            mDirectionDescriptions.clear();
-        }
-    }
-
-    /**
-     * Gets the resource id specifying the target direction descriptions.
-     *
-     * @return The resource id.
-     */
-    public int getDirectionDescriptionsResourceId() {
-        return mDirectionDescriptionsResourceId;
-    }
-
-    /**
-     * Enable or disable vibrate on touch.
-     *
-     * @param enabled
-     */
-    public void setVibrateEnabled(boolean enabled) {
-        if (enabled && mVibrator == null) {
-            mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
-        } else {
-            mVibrator = null;
-        }
-    }
-
-    /**
-     * Starts wave animation.
-     *
-     */
-    public void ping() {
-        if (mFeedbackCount > 0) {
-            boolean doWaveAnimation = true;
-            final AnimationBundle waveAnimations = mWaveAnimations;
-
-            // Don't do a wave if there's already one in progress
-            if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) {
-                long t = waveAnimations.get(0).animator.getCurrentPlayTime();
-                if (t < WAVE_ANIMATION_DURATION/2) {
-                    doWaveAnimation = false;
-                }
-            }
-
-            if (doWaveAnimation) {
-                startWaveAnimation();
-            }
-        }
-    }
-
-    private void stopAndHideWaveAnimation() {
-        mWaveAnimations.cancel();
-        mPointCloud.waveManager.setAlpha(0.0f);
-    }
-
-    private void startWaveAnimation() {
-        mWaveAnimations.cancel();
-        mPointCloud.waveManager.setAlpha(1.0f);
-        mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f);
-        mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION,
-                "ease", Ease.Quad.easeOut,
-                "delay", 0,
-                "radius", 2.0f * mOuterRadius,
-                "onUpdate", mUpdateListener,
-                "onComplete",
-                new AnimatorListenerAdapter() {
-                    public void onAnimationEnd(Animator animator) {
-                        mPointCloud.waveManager.setRadius(0.0f);
-                        mPointCloud.waveManager.setAlpha(0.0f);
-                    }
-                }));
-        mWaveAnimations.start();
-    }
-
-    /**
-     * Resets the widget to default state and cancels all animation. If animate is 'true', will
-     * animate objects into place. Otherwise, objects will snap back to place.
-     *
-     * @param animate
-     */
-    public void reset(boolean animate) {
-        mGlowAnimations.stop();
-        mTargetAnimations.stop();
-        startBackgroundAnimation(0, 0.0f);
-        stopAndHideWaveAnimation();
-        hideTargets(animate, false);
-        hideGlow(0, 0, 0.0f, null);
-        Tweener.reset();
-    }
-
-    private void startBackgroundAnimation(int duration, float alpha) {
-        final Drawable background = getBackground();
-        if (mAlwaysTrackFinger && background != null) {
-            if (mBackgroundAnimator != null) {
-                mBackgroundAnimator.animator.cancel();
-            }
-            mBackgroundAnimator = Tweener.to(background, duration,
-                    "ease", Ease.Cubic.easeIn,
-                    "alpha", (int)(255.0f * alpha),
-                    "delay", SHOW_ANIMATION_DELAY);
-            mBackgroundAnimator.animator.start();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        final int action = event.getActionMasked();
-        boolean handled = false;
-        switch (action) {
-            case MotionEvent.ACTION_POINTER_DOWN:
-            case MotionEvent.ACTION_DOWN:
-                if (DEBUG) Log.v(TAG, "*** DOWN ***");
-                handleDown(event);
-                handleMove(event);
-                handled = true;
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (DEBUG) Log.v(TAG, "*** MOVE ***");
-                handleMove(event);
-                handled = true;
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-            case MotionEvent.ACTION_UP:
-                if (DEBUG) Log.v(TAG, "*** UP ***");
-                handleMove(event);
-                handleUp(event);
-                handled = true;
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-                if (DEBUG) Log.v(TAG, "*** CANCEL ***");
-                handleMove(event);
-                handleCancel(event);
-                handled = true;
-                break;
-
-        }
-        invalidate();
-        return handled ? true : super.onTouchEvent(event);
-    }
-
-    private void updateGlowPosition(float x, float y) {
-        float dx = x - mOuterRing.getX();
-        float dy = y - mOuterRing.getY();
-        dx *= 1f / mRingScaleFactor;
-        dy *= 1f / mRingScaleFactor;
-        mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
-        mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
-    }
-
-    private void handleDown(MotionEvent event) {
-        int actionIndex = event.getActionIndex();
-        float eventX = event.getX(actionIndex);
-        float eventY = event.getY(actionIndex);
-        switchToState(STATE_START, eventX, eventY);
-        if (!trySwitchToFirstTouchState(eventX, eventY)) {
-            mDragging = false;
-        } else {
-            mPointerId = event.getPointerId(actionIndex);
-            updateGlowPosition(eventX, eventY);
-        }
-    }
-
-    private void handleUp(MotionEvent event) {
-        if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
-        int actionIndex = event.getActionIndex();
-        if (event.getPointerId(actionIndex) == mPointerId) {
-            switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
-        }
-    }
-
-    private void handleCancel(MotionEvent event) {
-        if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
-
-        // Drop the active target if canceled.
-        mActiveTarget = -1; 
-
-        int actionIndex = event.findPointerIndex(mPointerId);
-        actionIndex = actionIndex == -1 ? 0 : actionIndex;
-        switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
-    }
-
-    private void handleMove(MotionEvent event) {
-        int activeTarget = -1;
-        final int historySize = event.getHistorySize();
-        ArrayList<TargetDrawable> targets = mTargetDrawables;
-        int ntargets = targets.size();
-        float x = 0.0f;
-        float y = 0.0f;
-        float activeAngle = 0.0f;
-        int actionIndex = event.findPointerIndex(mPointerId);
-
-        if (actionIndex == -1) {
-            return;  // no data for this pointer
-        }
-
-        for (int k = 0; k < historySize + 1; k++) {
-            float eventX = k < historySize ? event.getHistoricalX(actionIndex, k)
-                    : event.getX(actionIndex);
-            float eventY = k < historySize ? event.getHistoricalY(actionIndex, k)
-                    : event.getY(actionIndex);
-            // tx and ty are relative to wave center
-            float tx = eventX - mWaveCenterX;
-            float ty = eventY - mWaveCenterY;
-            float touchRadius = (float) Math.hypot(tx, ty);
-            final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
-            float limitX = tx * scale;
-            float limitY = ty * scale;
-            double angleRad = Math.atan2(-ty, tx);
-
-            if (!mDragging) {
-                trySwitchToFirstTouchState(eventX, eventY);
-            }
-
-            if (mDragging) {
-                // For multiple targets, snap to the one that matches
-                final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
-                final float snapDistance2 = snapRadius * snapRadius;
-                // Find first target in range
-                for (int i = 0; i < ntargets; i++) {
-                    TargetDrawable target = targets.get(i);
-
-                    double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets;
-                    double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets;
-                    if (target.isEnabled()) {
-                        boolean angleMatches =
-                            (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
-                            (angleRad + 2 * Math.PI > targetMinRad &&
-                             angleRad + 2 * Math.PI <= targetMaxRad) ||
-                            (angleRad - 2 * Math.PI > targetMinRad &&
-                             angleRad - 2 * Math.PI <= targetMaxRad);
-                        if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
-                            activeTarget = i;
-                            activeAngle = (float) -angleRad;
-                        }
-                    }
-                }
-            }
-            x = limitX;
-            y = limitY;
-        }
-
-        if (!mDragging) {
-            return;
-        }
-
-        if (activeTarget != -1) {
-            switchToState(STATE_SNAP, x,y);
-            updateGlowPosition(x, y);
-        } else {
-            switchToState(STATE_TRACKING, x, y);
-            updateGlowPosition(x, y);
-        }
-
-        if (mActiveTarget != activeTarget) {
-            // Defocus the old target
-            if (mActiveTarget != -1) {
-                TargetDrawable target = targets.get(mActiveTarget);
-                if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
-                    target.setState(TargetDrawable.STATE_INACTIVE);
-                }
-                if (mMagneticTargets) {
-                    updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY);
-                }
-            }
-            // Focus the new target
-            if (activeTarget != -1) {
-                TargetDrawable target = targets.get(activeTarget);
-                if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
-                    target.setState(TargetDrawable.STATE_FOCUSED);
-                }
-                if (mMagneticTargets) {
-                    updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle);
-                }
-                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-                    String targetContentDescription = getTargetDescription(activeTarget);
-                    announceForAccessibility(targetContentDescription);
-                }
-            }
-        }
-        mActiveTarget = activeTarget;
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
-            final int action = event.getAction();
-            switch (action) {
-                case MotionEvent.ACTION_HOVER_ENTER:
-                    event.setAction(MotionEvent.ACTION_DOWN);
-                    break;
-                case MotionEvent.ACTION_HOVER_MOVE:
-                    event.setAction(MotionEvent.ACTION_MOVE);
-                    break;
-                case MotionEvent.ACTION_HOVER_EXIT:
-                    event.setAction(MotionEvent.ACTION_UP);
-                    break;
-            }
-            onTouchEvent(event);
-            event.setAction(action);
-        }
-        super.onHoverEvent(event);
-        return true;
-    }
-
-    /**
-     * Sets the current grabbed state, and dispatches a grabbed state change
-     * event to our listener.
-     */
-    private void setGrabbedState(int newState) {
-        if (newState != mGrabbedState) {
-            if (newState != OnTriggerListener.NO_HANDLE) {
-                vibrate();
-            }
-            mGrabbedState = newState;
-            if (mOnTriggerListener != null) {
-                if (newState == OnTriggerListener.NO_HANDLE) {
-                    mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
-                } else {
-                    mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
-                }
-                mOnTriggerListener.onGrabbedStateChange(this, newState);
-            }
-        }
-    }
-
-    private boolean trySwitchToFirstTouchState(float x, float y) {
-        final float tx = x - mWaveCenterX;
-        final float ty = y - mWaveCenterY;
-        if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) {
-            if (DEBUG) Log.v(TAG, "** Handle HIT");
-            switchToState(STATE_FIRST_TOUCH, x, y);
-            updateGlowPosition(tx, ty);
-            mDragging = true;
-            return true;
-        }
-        return false;
-    }
-
-    private void assignDefaultsIfNeeded() {
-        if (mOuterRadius == 0.0f) {
-            mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
-        }
-        if (mSnapMargin == 0.0f) {
-            mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
-        }
-        if (mInnerRadius == 0.0f) {
-            mInnerRadius = mHandleDrawable.getWidth() / 10.0f;
-        }
-    }
-
-    private void computeInsets(int dx, int dy) {
-        final int layoutDirection = getLayoutDirection();
-        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
-        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-            case Gravity.LEFT:
-                mHorizontalInset = 0;
-                break;
-            case Gravity.RIGHT:
-                mHorizontalInset = dx;
-                break;
-            case Gravity.CENTER_HORIZONTAL:
-            default:
-                mHorizontalInset = dx / 2;
-                break;
-        }
-        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
-            case Gravity.TOP:
-                mVerticalInset = 0;
-                break;
-            case Gravity.BOTTOM:
-                mVerticalInset = dy;
-                break;
-            case Gravity.CENTER_VERTICAL:
-            default:
-                mVerticalInset = dy / 2;
-                break;
-        }
-    }
-
-    /**
-     * Given the desired width and height of the ring and the allocated width and height, compute
-     * how much we need to scale the ring.
-     */
-    private float computeScaleFactor(int desiredWidth, int desiredHeight,
-            int actualWidth, int actualHeight) {
-
-        // Return unity if scaling is not allowed.
-        if (!mAllowScaling) return 1f;
-
-        final int layoutDirection = getLayoutDirection();
-        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
-        float scaleX = 1f;
-        float scaleY = 1f;
-
-        // We use the gravity as a cue for whether we want to scale on a particular axis.
-        // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
-        // we only scale to fit vertically if we're not pinned to the top or bottom. In these
-        // cases, we want the ring to hang off the side or top/bottom, respectively.
-        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-            case Gravity.LEFT:
-            case Gravity.RIGHT:
-                break;
-            case Gravity.CENTER_HORIZONTAL:
-            default:
-                if (desiredWidth > actualWidth) {
-                    scaleX = (1f * actualWidth - mMaxTargetWidth) /
-                            (desiredWidth - mMaxTargetWidth);
-                }
-                break;
-        }
-        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
-            case Gravity.TOP:
-            case Gravity.BOTTOM:
-                break;
-            case Gravity.CENTER_VERTICAL:
-            default:
-                if (desiredHeight > actualHeight) {
-                    scaleY = (1f * actualHeight - mMaxTargetHeight) /
-                            (desiredHeight - mMaxTargetHeight);
-                }
-                break;
-        }
-        return Math.min(scaleX, scaleY);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int minimumWidth = getSuggestedMinimumWidth();
-        final int minimumHeight = getSuggestedMinimumHeight();
-        int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
-        int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
-        mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
-                computedWidth, computedHeight);
-
-        int scaledWidth = getScaledSuggestedMinimumWidth();
-        int scaledHeight = getScaledSuggestedMinimumHeight();
-
-        computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
-        setMeasuredDimension(computedWidth, computedHeight);
-    }
-
-    private float getRingWidth() {
-        return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
-    }
-
-    private float getRingHeight() {
-        return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        final int width = right - left;
-        final int height = bottom - top;
-
-        // Target placement width/height. This puts the targets on the greater of the ring
-        // width or the specified outer radius.
-        final float placementWidth = getRingWidth();
-        final float placementHeight = getRingHeight();
-        float newWaveCenterX = mHorizontalInset
-                + Math.max(width, mMaxTargetWidth + placementWidth) / 2;
-        float newWaveCenterY = mVerticalInset
-                + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;
-
-        if (mInitialLayout) {
-            stopAndHideWaveAnimation();
-            hideTargets(false, false);
-            mInitialLayout = false;
-        }
-
-        mOuterRing.setPositionX(newWaveCenterX);
-        mOuterRing.setPositionY(newWaveCenterY);
-
-        mPointCloud.setScale(mRingScaleFactor);
-
-        mHandleDrawable.setPositionX(newWaveCenterX);
-        mHandleDrawable.setPositionY(newWaveCenterY);
-
-        updateTargetPositions(newWaveCenterX, newWaveCenterY);
-        updatePointCloudPosition(newWaveCenterX, newWaveCenterY);
-        updateGlowPosition(newWaveCenterX, newWaveCenterY);
-
-        mWaveCenterX = newWaveCenterX;
-        mWaveCenterY = newWaveCenterY;
-
-        if (DEBUG) dump();
-    }
-
-    private void updateTargetPosition(int i, float centerX, float centerY) {
-        final float angle = getAngle(getSliceAngle(), i);
-        updateTargetPosition(i, centerX, centerY, angle);
-    }
-
-    private void updateTargetPosition(int i, float centerX, float centerY, float angle) {
-        final float placementRadiusX = getRingWidth() / 2;
-        final float placementRadiusY = getRingHeight() / 2;
-        if (i >= 0) {
-            ArrayList<TargetDrawable> targets = mTargetDrawables;
-            final TargetDrawable targetIcon = targets.get(i);
-            targetIcon.setPositionX(centerX);
-            targetIcon.setPositionY(centerY);
-            targetIcon.setX(placementRadiusX * (float) Math.cos(angle));
-            targetIcon.setY(placementRadiusY * (float) Math.sin(angle));
-        }
-    }
-
-    private void updateTargetPositions(float centerX, float centerY) {
-        updateTargetPositions(centerX, centerY, false);
-    }
-
-    private void updateTargetPositions(float centerX, float centerY, boolean skipActive) {
-        final int size = mTargetDrawables.size();
-        final float alpha = getSliceAngle();
-        // Reposition the target drawables if the view changed.
-        for (int i = 0; i < size; i++) {
-            if (!skipActive || i != mActiveTarget) {
-                updateTargetPosition(i, centerX, centerY, getAngle(alpha, i));
-            }
-        }
-    }
-
-    private float getAngle(float alpha, int i) {
-        return mFirstItemOffset + alpha * i;
-    }
-
-    private float getSliceAngle() {
-        return (float) (-2.0f * Math.PI / mTargetDrawables.size());
-    }
-
-    private void updatePointCloudPosition(float centerX, float centerY) {
-        mPointCloud.setCenter(centerX, centerY);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        mPointCloud.draw(canvas);
-        mOuterRing.draw(canvas);
-        final int ntargets = mTargetDrawables.size();
-        for (int i = 0; i < ntargets; i++) {
-            TargetDrawable target = mTargetDrawables.get(i);
-            if (target != null) {
-                target.draw(canvas);
-            }
-        }
-        mHandleDrawable.draw(canvas);
-    }
-
-    public void setOnTriggerListener(OnTriggerListener listener) {
-        mOnTriggerListener = listener;
-    }
-
-    private float square(float d) {
-        return d * d;
-    }
-
-    private float dist2(float dx, float dy) {
-        return dx*dx + dy*dy;
-    }
-
-    private float getScaledGlowRadiusSquared() {
-        final float scaledTapRadius;
-        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
-            scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius;
-        } else {
-            scaledTapRadius = mGlowRadius;
-        }
-        return square(scaledTapRadius);
-    }
-
-    private void announceTargets() {
-        StringBuilder utterance = new StringBuilder();
-        final int targetCount = mTargetDrawables.size();
-        for (int i = 0; i < targetCount; i++) {
-            String targetDescription = getTargetDescription(i);
-            String directionDescription = getDirectionDescription(i);
-            if (!TextUtils.isEmpty(targetDescription)
-                    && !TextUtils.isEmpty(directionDescription)) {
-                String text = String.format(directionDescription, targetDescription);
-                utterance.append(text);
-            }
-        }
-        if (utterance.length() > 0) {
-            announceForAccessibility(utterance.toString());
-        }
-    }
-
-    private String getTargetDescription(int index) {
-        if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
-            mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
-            if (mTargetDrawables.size() != mTargetDescriptions.size()) {
-                Log.w(TAG, "The number of target drawables must be"
-                        + " equal to the number of target descriptions.");
-                return null;
-            }
-        }
-        return mTargetDescriptions.get(index);
-    }
-
-    private String getDirectionDescription(int index) {
-        if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
-            mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
-            if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
-                Log.w(TAG, "The number of target drawables must be"
-                        + " equal to the number of direction descriptions.");
-                return null;
-            }
-        }
-        return mDirectionDescriptions.get(index);
-    }
-
-    private ArrayList<String> loadDescriptions(int resourceId) {
-        TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
-        final int count = array.length();
-        ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
-        for (int i = 0; i < count; i++) {
-            String contentDescription = array.getString(i);
-            targetContentDescriptions.add(contentDescription);
-        }
-        array.recycle();
-        return targetContentDescriptions;
-    }
-
-    public int getResourceIdForTarget(int index) {
-        final TargetDrawable drawable = mTargetDrawables.get(index);
-        return drawable == null ? 0 : drawable.getResourceId();
-    }
-
-    public void setEnableTarget(int resourceId, boolean enabled) {
-        for (int i = 0; i < mTargetDrawables.size(); i++) {
-            final TargetDrawable target = mTargetDrawables.get(i);
-            if (target.getResourceId() == resourceId) {
-                target.setEnabled(enabled);
-                break; // should never be more than one match
-            }
-        }
-    }
-
-    /**
-     * Gets the position of a target in the array that matches the given resource.
-     * @param resourceId
-     * @return the index or -1 if not found
-     */
-    public int getTargetPosition(int resourceId) {
-        for (int i = 0; i < mTargetDrawables.size(); i++) {
-            final TargetDrawable target = mTargetDrawables.get(i);
-            if (target.getResourceId() == resourceId) {
-                return i; // should never be more than one match
-            }
-        }
-        return -1;
-    }
-
-    private boolean replaceTargetDrawables(Resources res, int existingResourceId,
-            int newResourceId) {
-        if (existingResourceId == 0 || newResourceId == 0) {
-            return false;
-        }
-
-        boolean result = false;
-        final ArrayList<TargetDrawable> drawables = mTargetDrawables;
-        final int size = drawables.size();
-        for (int i = 0; i < size; i++) {
-            final TargetDrawable target = drawables.get(i);
-            if (target != null && target.getResourceId() == existingResourceId) {
-                target.setDrawable(res, newResourceId);
-                result = true;
-            }
-        }
-
-        if (result) {
-            requestLayout(); // in case any given drawable's size changes
-        }
-
-        return result;
-    }
-
-    /**
-     * Searches the given package for a resource to use to replace the Drawable on the
-     * target with the given resource id
-     * @param component of the .apk that contains the resource
-     * @param name of the metadata in the .apk
-     * @param existingResId the resource id of the target to search for
-     * @return true if found in the given package and replaced at least one target Drawables
-     */
-    public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
-                int existingResId) {
-        if (existingResId == 0) return false;
-
-        boolean replaced = false;
-        if (component != null) {
-            try {
-                PackageManager packageManager = mContext.getPackageManager();
-                // Look for the search icon specified in the activity meta-data
-                Bundle metaData = packageManager.getActivityInfo(
-                        component, PackageManager.GET_META_DATA).metaData;
-                if (metaData != null) {
-                    int iconResId = metaData.getInt(name);
-                    if (iconResId != 0) {
-                        Resources res = packageManager.getResourcesForActivity(component);
-                        replaced = replaceTargetDrawables(res, existingResId, iconResId);
-                    }
-                }
-            } catch (NameNotFoundException e) {
-                Log.w(TAG, "Failed to swap drawable; "
-                        + component.flattenToShortString() + " not found", e);
-            } catch (Resources.NotFoundException nfe) {
-                Log.w(TAG, "Failed to swap drawable from "
-                        + component.flattenToShortString(), nfe);
-            }
-        }
-        if (!replaced) {
-            // Restore the original drawable
-            replaceTargetDrawables(mContext.getResources(), existingResId, existingResId);
-        }
-        return replaced;
-    }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java b/core/java/com/android/internal/widget/multiwaveview/PointCloud.java
deleted file mode 100644
index 6f26b99..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget.multiwaveview;
-
-import java.util.ArrayList;
-
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-public class PointCloud {
-    private static final float MIN_POINT_SIZE = 2.0f;
-    private static final float MAX_POINT_SIZE = 4.0f;
-    private static final int INNER_POINTS = 8;
-    private static final String TAG = "PointCloud";
-    private ArrayList<Point> mPointCloud = new ArrayList<Point>();
-    private Drawable mDrawable;
-    private float mCenterX;
-    private float mCenterY;
-    private Paint mPaint;
-    private float mScale = 1.0f;
-    private static final float PI = (float) Math.PI;
-
-    // These allow us to have multiple concurrent animations.
-    WaveManager waveManager = new WaveManager();
-    GlowManager glowManager = new GlowManager();
-    private float mOuterRadius;
-
-    public class WaveManager {
-        private float radius = 50;
-        private float alpha = 0.0f;
-
-        public void setRadius(float r) {
-            radius = r;
-        }
-
-        public float getRadius() {
-            return radius;
-        }
-
-        public void setAlpha(float a) {
-            alpha = a;
-        }
-
-        public float getAlpha() {
-            return alpha;
-        }
-    };
-
-    public class GlowManager {
-        private float x;
-        private float y;
-        private float radius = 0.0f;
-        private float alpha = 0.0f;
-
-        public void setX(float x1) {
-            x = x1;
-        }
-
-        public float getX() {
-            return x;
-        }
-
-        public void setY(float y1) {
-            y = y1;
-        }
-
-        public float getY() {
-            return y;
-        }
-
-        public void setAlpha(float a) {
-            alpha = a;
-        }
-
-        public float getAlpha() {
-            return alpha;
-        }
-
-        public void setRadius(float r) {
-            radius = r;
-        }
-
-        public float getRadius() {
-            return radius;
-        }
-    }
-
-    class Point {
-        float x;
-        float y;
-        float radius;
-
-        public Point(float x2, float y2, float r) {
-            x = (float) x2;
-            y = (float) y2;
-            radius = r;
-        }
-    }
-
-    public PointCloud(Drawable drawable) {
-        mPaint = new Paint();
-        mPaint.setFilterBitmap(true);
-        mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable
-        mPaint.setAntiAlias(true);
-        mPaint.setDither(true);
-
-        mDrawable = drawable;
-        if (mDrawable != null) {
-            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
-        }
-    }
-
-    public void setCenter(float x, float y) {
-        mCenterX = x;
-        mCenterY = y;
-    }
-
-    public void makePointCloud(float innerRadius, float outerRadius) {
-        if (innerRadius == 0) {
-            Log.w(TAG, "Must specify an inner radius");
-            return;
-        }
-        mOuterRadius = outerRadius;
-        mPointCloud.clear();
-        final float pointAreaRadius =  (outerRadius - innerRadius);
-        final float ds = (2.0f * PI * innerRadius / INNER_POINTS);
-        final int bands = (int) Math.round(pointAreaRadius / ds);
-        final float dr = pointAreaRadius / bands;
-        float r = innerRadius;
-        for (int b = 0; b <= bands; b++, r += dr) {
-            float circumference = 2.0f * PI * r;
-            final int pointsInBand = (int) (circumference / ds);
-            float eta = PI/2.0f;
-            float dEta = 2.0f * PI / pointsInBand;
-            for (int i = 0; i < pointsInBand; i++) {
-                float x = r * (float) Math.cos(eta);
-                float y = r * (float) Math.sin(eta);
-                eta += dEta;
-                mPointCloud.add(new Point(x, y, r));
-            }
-        }
-    }
-
-    public void setScale(float scale) {
-        mScale  = scale;
-    }
-
-    public float getScale() {
-        return mScale;
-    }
-
-    public int getAlphaForPoint(Point point) {
-        // Contribution from positional glow
-        float glowDistance = (float) Math.hypot(glowManager.x - point.x, glowManager.y - point.y);
-        float glowAlpha = 0.0f;
-        if (glowDistance < glowManager.radius) {
-            float cosf = (float) Math.cos(PI * 0.25f * glowDistance / glowManager.radius);
-            glowAlpha = glowManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 10.0f));
-        }
-
-        // Compute contribution from Wave
-        float radius = (float) Math.hypot(point.x, point.y);
-        float waveAlpha = 0.0f;
-        if (radius < waveManager.radius * 2) {
-            float distanceToWaveRing = (radius - waveManager.radius);
-            float cosf = (float) Math.cos(PI * 0.5f * distanceToWaveRing / waveManager.radius);
-            waveAlpha = waveManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 6.0f));
-        }
-        return (int) (Math.max(glowAlpha, waveAlpha) * 255);
-    }
-
-    private float interp(float min, float max, float f) {
-        return min + (max - min) * f;
-    }
-
-    public void draw(Canvas canvas) {
-        ArrayList<Point> points = mPointCloud;
-        canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        canvas.scale(mScale, mScale, mCenterX, mCenterY);
-        for (int i = 0; i < points.size(); i++) {
-            Point point = points.get(i);
-            final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE,
-                    point.radius / mOuterRadius);
-            final float px = point.x + mCenterX;
-            final float py = point.y + mCenterY;
-            int alpha = getAlphaForPoint(point);
-
-            if (alpha == 0) continue;
-
-            if (mDrawable != null) {
-                canvas.save(Canvas.MATRIX_SAVE_FLAG);
-                final float cx = mDrawable.getIntrinsicWidth() * 0.5f;
-                final float cy = mDrawable.getIntrinsicHeight() * 0.5f;
-                final float s = pointSize / MAX_POINT_SIZE;
-                canvas.scale(s, s, px, py);
-                canvas.translate(px - cx, py - cy);
-                mDrawable.setAlpha(alpha);
-                mDrawable.draw(canvas);
-                canvas.restore();
-            } else {
-                mPaint.setAlpha(alpha);
-                canvas.drawCircle(px, py, pointSize, mPaint);
-            }
-        }
-        canvas.restore();
-    }
-
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
deleted file mode 100644
index 5a4c441..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ /dev/null
@@ -1,229 +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.internal.widget.multiwaveview;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.Log;
-
-public class TargetDrawable {
-    private static final String TAG = "TargetDrawable";
-    private static final boolean DEBUG = false;
-
-    public static final int[] STATE_ACTIVE =
-            { android.R.attr.state_enabled, android.R.attr.state_active };
-    public static final int[] STATE_INACTIVE =
-            { android.R.attr.state_enabled, -android.R.attr.state_active };
-    public static final int[] STATE_FOCUSED =
-            { android.R.attr.state_enabled, -android.R.attr.state_active,
-                android.R.attr.state_focused };
-
-    private float mTranslationX = 0.0f;
-    private float mTranslationY = 0.0f;
-    private float mPositionX = 0.0f;
-    private float mPositionY = 0.0f;
-    private float mScaleX = 1.0f;
-    private float mScaleY = 1.0f;
-    private float mAlpha = 1.0f;
-    private Drawable mDrawable;
-    private boolean mEnabled = true;
-    private final int mResourceId;
-
-    public TargetDrawable(Resources res, int resId) {
-        mResourceId = resId;
-        setDrawable(res, resId);
-    }
-
-    public void setDrawable(Resources res, int resId) {
-        // Note we explicitly don't set mResourceId to resId since we allow the drawable to be
-        // swapped at runtime and want to re-use the existing resource id for identification.
-        Drawable drawable = resId == 0 ? null : res.getDrawable(resId);
-        // Mutate the drawable so we can animate shared drawable properties.
-        mDrawable = drawable != null ? drawable.mutate() : null;
-        resizeDrawables();
-        setState(STATE_INACTIVE);
-    }
-
-    public TargetDrawable(TargetDrawable other) {
-        mResourceId = other.mResourceId;
-        // Mutate the drawable so we can animate shared drawable properties.
-        mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null;
-        resizeDrawables();
-        setState(STATE_INACTIVE);
-    }
-
-    public void setState(int [] state) {
-        if (mDrawable instanceof StateListDrawable) {
-            StateListDrawable d = (StateListDrawable) mDrawable;
-            d.setState(state);
-        }
-    }
-
-    public boolean hasState(int [] state) {
-        if (mDrawable instanceof StateListDrawable) {
-            StateListDrawable d = (StateListDrawable) mDrawable;
-            // TODO: this doesn't seem to work
-            return d.getStateDrawableIndex(state) != -1;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the drawable is a StateListDrawable and is in the focused state.
-     *
-     * @return
-     */
-    public boolean isActive() {
-        if (mDrawable instanceof StateListDrawable) {
-            StateListDrawable d = (StateListDrawable) mDrawable;
-            int[] states = d.getState();
-            for (int i = 0; i < states.length; i++) {
-                if (states[i] == android.R.attr.state_focused) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if this target is enabled. Typically an enabled target contains a valid
-     * drawable in a valid state. Currently all targets with valid drawables are valid.
-     *
-     * @return
-     */
-    public boolean isEnabled() {
-        return mDrawable != null && mEnabled;
-    }
-
-    /**
-     * Makes drawables in a StateListDrawable all the same dimensions.
-     * If not a StateListDrawable, then justs sets the bounds to the intrinsic size of the
-     * drawable.
-     */
-    private void resizeDrawables() {
-        if (mDrawable instanceof StateListDrawable) {
-            StateListDrawable d = (StateListDrawable) mDrawable;
-            int maxWidth = 0;
-            int maxHeight = 0;
-            for (int i = 0; i < d.getStateCount(); i++) {
-                Drawable childDrawable = d.getStateDrawable(i);
-                maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
-                maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
-            }
-            if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
-                        + maxWidth + "x" + maxHeight);
-            d.setBounds(0, 0, maxWidth, maxHeight);
-            for (int i = 0; i < d.getStateCount(); i++) {
-                Drawable childDrawable = d.getStateDrawable(i);
-                if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
-                            + maxWidth + "x" + maxHeight);
-                childDrawable.setBounds(0, 0, maxWidth, maxHeight);
-            }
-        } else if (mDrawable != null) {
-            mDrawable.setBounds(0, 0,
-                    mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
-        }
-    }
-
-    public void setX(float x) {
-        mTranslationX = x;
-    }
-
-    public void setY(float y) {
-        mTranslationY = y;
-    }
-
-    public void setScaleX(float x) {
-        mScaleX = x;
-    }
-
-    public void setScaleY(float y) {
-        mScaleY = y;
-    }
-
-    public void setAlpha(float alpha) {
-        mAlpha = alpha;
-    }
-
-    public float getX() {
-        return mTranslationX;
-    }
-
-    public float getY() {
-        return mTranslationY;
-    }
-
-    public float getScaleX() {
-        return mScaleX;
-    }
-
-    public float getScaleY() {
-        return mScaleY;
-    }
-
-    public float getAlpha() {
-        return mAlpha;
-    }
-
-    public void setPositionX(float x) {
-        mPositionX = x;
-    }
-
-    public void setPositionY(float y) {
-        mPositionY = y;
-    }
-
-    public float getPositionX() {
-        return mPositionX;
-    }
-
-    public float getPositionY() {
-        return mPositionY;
-    }
-
-    public int getWidth() {
-        return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0;
-    }
-
-    public int getHeight() {
-        return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0;
-    }
-
-    public void draw(Canvas canvas) {
-        if (mDrawable == null || !mEnabled) {
-            return;
-        }
-        canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY);
-        canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY);
-        canvas.translate(-0.5f * getWidth(), -0.5f * getHeight());
-        mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
-        mDrawable.draw(canvas);
-        canvas.restore();
-    }
-
-    public void setEnabled(boolean enabled) {
-        mEnabled  = enabled;
-    }
-
-    public int getResourceId() {
-        return mResourceId;
-    }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
deleted file mode 100644
index d559d9d..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ /dev/null
@@ -1,177 +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.internal.widget.multiwaveview;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map.Entry;
-
-import android.animation.Animator.AnimatorListener;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.util.Log;
-
-class Tweener {
-    private static final String TAG = "Tweener";
-    private static final boolean DEBUG = false;
-
-    ObjectAnimator animator;
-    private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();
-
-    public Tweener(ObjectAnimator anim) {
-        animator = anim;
-    }
-
-    private static void remove(Animator animator) {
-        Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
-        while (iter.hasNext()) {
-            Entry<Object, Tweener> entry = iter.next();
-            if (entry.getValue().animator == animator) {
-                if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
-                        + " sTweens.size() = " + sTweens.size());
-                iter.remove();
-                break; // an animator can only be attached to one object
-            }
-        }
-    }
-
-    public static Tweener to(Object object, long duration, Object... vars) {
-        long delay = 0;
-        AnimatorUpdateListener updateListener = null;
-        AnimatorListener listener = null;
-        TimeInterpolator interpolator = null;
-
-        // Iterate through arguments and discover properties to animate
-        ArrayList<PropertyValuesHolder> props = new ArrayList<PropertyValuesHolder>(vars.length/2);
-        for (int i = 0; i < vars.length; i+=2) {
-            if (!(vars[i] instanceof String)) {
-                throw new IllegalArgumentException("Key must be a string: " + vars[i]);
-            }
-            String key = (String) vars[i];
-            Object value = vars[i+1];
-            if ("simultaneousTween".equals(key)) {
-                // TODO
-            } else if ("ease".equals(key)) {
-                interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
-            } else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
-                updateListener = (AnimatorUpdateListener) value;
-            } else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
-                listener = (AnimatorListener) value;
-            } else if ("delay".equals(key)) {
-                delay = ((Number) value).longValue();
-            } else if ("syncWith".equals(key)) {
-                // TODO
-            } else if (value instanceof float[]) {
-                props.add(PropertyValuesHolder.ofFloat(key,
-                        ((float[])value)[0], ((float[])value)[1]));
-            } else if (value instanceof int[]) {
-                props.add(PropertyValuesHolder.ofInt(key,
-                        ((int[])value)[0], ((int[])value)[1]));
-            } else if (value instanceof Number) {
-                float floatValue = ((Number)value).floatValue();
-                props.add(PropertyValuesHolder.ofFloat(key, floatValue));
-            } else {
-                throw new IllegalArgumentException(
-                        "Bad argument for key \"" + key + "\" with value " + value.getClass());
-            }
-        }
-
-        // Re-use existing tween, if present
-        Tweener tween = sTweens.get(object);
-        ObjectAnimator anim = null;
-        if (tween == null) {
-            anim = ObjectAnimator.ofPropertyValuesHolder(object,
-                    props.toArray(new PropertyValuesHolder[props.size()]));
-            tween = new Tweener(anim);
-            sTweens.put(object, tween);
-            if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
-        } else {
-            anim = sTweens.get(object).animator;
-            replace(props, object); // Cancel all animators for given object
-        }
-
-        if (interpolator != null) {
-            anim.setInterpolator(interpolator);
-        }
-
-        // Update animation with properties discovered in loop above
-        anim.setStartDelay(delay);
-        anim.setDuration(duration);
-        if (updateListener != null) {
-            anim.removeAllUpdateListeners(); // There should be only one
-            anim.addUpdateListener(updateListener);
-        }
-        if (listener != null) {
-            anim.removeAllListeners(); // There should be only one.
-            anim.addListener(listener);
-        }
-        anim.addListener(mCleanupListener);
-
-        return tween;
-    }
-
-    Tweener from(Object object, long duration, Object... vars) {
-        // TODO:  for v of vars
-        //            toVars[v] = object[v]
-        //            object[v] = vars[v]
-        return Tweener.to(object, duration, vars);
-    }
-
-    // Listener to watch for completed animations and remove them.
-    private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            remove(animation);
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            remove(animation);
-        }
-    };
-
-    public static void reset() {
-        if (DEBUG) {
-            Log.v(TAG, "Reset()");
-            if (sTweens.size() > 0) {
-                Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
-            }
-        }
-        sTweens.clear();
-    }
-
-    private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
-        for (final Object killobject : args) {
-            Tweener tween = sTweens.get(killobject);
-            if (tween != null) {
-                tween.animator.cancel();
-                if (props != null) {
-                    tween.animator.setValues(
-                            props.toArray(new PropertyValuesHolder[props.size()]));
-                } else {
-                    sTweens.remove(tween);
-                }
-            }
-        }
-    }
-}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index cb9b421..1dd10e1 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -87,6 +87,7 @@
     android_graphics_Canvas.cpp \
     android_graphics_Picture.cpp \
     android/graphics/AutoDecodeCancel.cpp \
+    android/graphics/AvoidXfermode.cpp \
     android/graphics/Bitmap.cpp \
     android/graphics/BitmapFactory.cpp \
     android/graphics/Camera.cpp \
diff --git a/core/jni/android/graphics/AvoidXfermode.cpp b/core/jni/android/graphics/AvoidXfermode.cpp
new file mode 100644
index 0000000..9ca1f26
--- /dev/null
+++ b/core/jni/android/graphics/AvoidXfermode.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "AvoidXfermode.h"
+#include "SkColorPriv.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkString.h"
+
+AvoidXfermode::AvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) {
+    if (tolerance > 255) {
+        tolerance = 255;
+    }
+    fTolerance = SkToU8(tolerance);
+    fOpColor = opColor;
+    fDistMul = (256 << 14) / (tolerance + 1);
+    fMode = mode;
+}
+
+SkFlattenable* AvoidXfermode::CreateProc(SkReadBuffer& buffer) {
+    const SkColor color = buffer.readColor();
+    const unsigned tolerance = buffer.readUInt();
+    const unsigned mode = buffer.readUInt();
+    return Create(color, tolerance, (Mode)mode);
+}
+
+void AvoidXfermode::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeColor(fOpColor);
+    buffer.writeUInt(fTolerance);
+    buffer.writeUInt(fMode);
+}
+
+// returns 0..31
+static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) {
+    SkASSERT(r <= SK_R16_MASK);
+    SkASSERT(g <= SK_G16_MASK);
+    SkASSERT(b <= SK_B16_MASK);
+
+    unsigned dr = SkAbs32(SkGetPackedR16(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS);
+    unsigned db = SkAbs32(SkGetPackedB16(c) - b);
+
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+// returns 0..255
+static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) {
+    SkASSERT(r <= 0xFF);
+    SkASSERT(g <= 0xFF);
+    SkASSERT(b <= 0xFF);
+
+    unsigned dr = SkAbs32(SkGetPackedR32(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG32(c) - g);
+    unsigned db = SkAbs32(SkGetPackedB32(c) - b);
+
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+static int scale_dist_14(int dist, uint32_t mul, uint32_t sub) {
+    int tmp = dist * mul - sub;
+    int result = (tmp + (1 << 13)) >> 14;
+
+    return result;
+}
+
+static inline unsigned Accurate255To256(unsigned x) {
+    return x + (x >> 7);
+}
+
+void AvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[]) const {
+    unsigned    opR = SkColorGetR(fOpColor);
+    unsigned    opG = SkColorGetG(fOpColor);
+    unsigned    opB = SkColorGetB(fOpColor);
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << 8;
+
+    int MAX, mask;
+
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 255;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+
+    for (int i = 0; i < count; i++) {
+        int d = color_dist32(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 255);
+        d = Accurate255To256(d);
+
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 256);
+
+        if (d > 0) {
+            if (aa) {
+                d = SkAlphaMul(d, Accurate255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkFourByteInterp256(src[i], dst[i], d);
+        }
+    }
+}
+
+static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) {
+    SkASSERT(scale <= 32);
+    scale <<= 3;
+
+    return SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale),
+                        SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale),
+                        SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale));
+}
+
+void AvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[]) const {
+    unsigned    opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS);
+    unsigned    opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS);
+    unsigned    opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS);
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << SK_R16_BITS;
+
+    int MAX, mask;
+
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 31;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+
+    for (int i = 0; i < count; i++) {
+        int d = color_dist16(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 31);
+        // convert from 0..31 to 0..32
+        d += d >> 4;
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 32);
+
+        if (d > 0) {
+            if (aa) {
+                d = SkAlphaMul(d, Accurate255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkBlend3216(src[i], dst[i], d);
+        }
+    }
+}
+
+void AvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+        const SkAlpha aa[]) const {
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void AvoidXfermode::toString(SkString* str) const {
+    str->append("AvoidXfermode: opColor: ");
+    str->appendHex(fOpColor);
+    str->appendf("distMul: %d ", fDistMul);
+
+    static const char* gModeStrings[] = { "Avoid", "Target" };
+
+    str->appendf("mode: %s", gModeStrings[fMode]);
+}
+#endif
diff --git a/core/jni/android/graphics/AvoidXfermode.h b/core/jni/android/graphics/AvoidXfermode.h
new file mode 100644
index 0000000..318d7be
--- /dev/null
+++ b/core/jni/android/graphics/AvoidXfermode.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef AvoidXfermode_DEFINED
+#define AvoidXfermode_DEFINED
+
+#include "SkColor.h"
+#include "SkTypes.h"
+#include "SkXfermode.h"
+
+/** \class AvoidXfermode
+
+    This xfermode will draw the src everywhere except on top of the specified
+    color.
+*/
+class AvoidXfermode : public SkXfermode {
+public:
+    enum Mode {
+        kAvoidColor_Mode,   //!< draw everywhere except on the opColor
+        kTargetColor_Mode   //!< draw only on top of the opColor
+    };
+
+    /** This xfermode draws, or doesn't draw, based on the destination's
+        distance from an op-color.
+
+        There are two modes, and each mode interprets a tolerance value.
+
+        Avoid: In this mode, drawing is allowed only on destination pixels that
+               are different from the op-color.
+               Tolerance near 0: avoid any colors even remotely similar to the op-color
+               Tolerance near 255: avoid only colors nearly identical to the op-color
+
+        Target: In this mode, drawing only occurs on destination pixels that
+                are similar to the op-color
+                Tolerance near 0: draw only on colors that are nearly identical to the op-color
+                Tolerance near 255: draw on any colors even remotely similar to the op-color
+     */
+    static AvoidXfermode* Create(SkColor opColor, U8CPU tolerance, Mode mode) {
+        return SkNEW_ARGS(AvoidXfermode, (opColor, tolerance, mode));
+    }
+
+    // overrides from SkXfermode
+    void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+            const SkAlpha aa[]) const override;
+    void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+            const SkAlpha aa[]) const override;
+    void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+            const SkAlpha aa[]) const override;
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(AvoidXfermode)
+
+protected:
+    AvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode);
+    void flatten(SkWriteBuffer&) const override;
+
+private:
+    SkColor     fOpColor;
+    uint32_t    fDistMul;   // x.14 cached from fTolerance
+    uint8_t     fTolerance;
+    Mode        fMode;
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index b572604..036ece1 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -96,7 +96,7 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
     v->applyToCanvas(canvas);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 9996ce1..dde1393 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -366,7 +366,7 @@
     if (!canvasHandle) {
         return NULL;
     }
-    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
     SkASSERT(c);
     return c;
 }
@@ -511,18 +511,15 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool computeAllocationSize(const SkImageInfo& info, size_t* size, size_t* rowBytes) {
-        int32_t rowBytes32 = SkToS32(info.minRowBytes());
-        int64_t bigSize = (int64_t)info.height() * rowBytes32;
-        if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
-            return false; // allocation will be too large
-        }
+static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
+    int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
+    int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
+    if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
+        return false; // allocation will be too large
+    }
 
-        *size = sk_64_asS32(bigSize);
-        *rowBytes = rowBytes32;
-
-        SkASSERT(*size >= info.getSafeSize(*rowBytes));
-        return true;
+    *size = sk_64_asS32(bigSize);
+    return true;
 }
 
 jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
@@ -533,11 +530,15 @@
         return NULL;
     }
 
-    size_t size, rowBytes;
-    if (!computeAllocationSize(info, &size, &rowBytes)) {
+    size_t size;
+    if (!computeAllocationSize(*bitmap, &size)) {
         return NULL;
     }
 
+    // we must respect the rowBytes value already set on the bitmap instead of
+    // attempting to compute our own.
+    const size_t rowBytes = bitmap->rowBytes();
+
     jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,
                                                              gVMRuntime_newNonMovableArray,
                                                              gByte_class, size);
@@ -580,11 +581,15 @@
         return NULL;
     }
 
-    size_t size, rowBytes;
-    if (!computeAllocationSize(info, &size, &rowBytes)) {
+    size_t size;
+    if (!computeAllocationSize(*bitmap, &size)) {
         return false;
     }
 
+    // we must respect the rowBytes value already set on the bitmap instead of
+    // attempting to compute our own.
+    const size_t rowBytes = bitmap->rowBytes();
+
     void* addr = sk_malloc_flags(size, 0);
     if (NULL == addr) {
         return false;
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 543323c..3f8bfe2 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -120,7 +120,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->asSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const Paint* paint     = reinterpret_cast<Paint*>(paintHandle);
@@ -139,7 +139,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->asSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const Paint* paint     = reinterpret_cast<Paint*>(paintHandle);
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 2644888..6c32a21 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -24,7 +24,9 @@
     if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) {
         Res_png_9patch* patch = (Res_png_9patch*) data;
         size_t patchSize = patch->serializedSize();
-        assert(length == patchSize);
+        if (length != patchSize) {
+            return false;
+        }
         // You have to copy the data because it is owned by the png reader
         Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
         memcpy(patchNew, patch, patchSize);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index c249012..12bfaa2 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -101,7 +101,7 @@
     }
     validate();
     if (NULL != mPicture.get()) {
-        mPicture.get()->playback(canvas->getSkCanvas());
+        mPicture.get()->playback(canvas->asSkCanvas());
     }
 }
 
diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp
index 5a3883a..4a424ae 100644
--- a/core/jni/android/graphics/Xfermode.cpp
+++ b/core/jni/android/graphics/Xfermode.cpp
@@ -18,7 +18,7 @@
 #include "GraphicsJNI.h"
 #include "core_jni_helpers.h"
 
-#include "SkAvoidXfermode.h"
+#include "AvoidXfermode.h"
 #include "SkPixelXorXfermode.h"
 
 namespace android {
@@ -35,8 +35,8 @@
     static jlong avoid_create(JNIEnv* env, jobject, jint opColor,
                                 jint tolerance, jint modeHandle)
     {
-        SkAvoidXfermode::Mode mode = static_cast<SkAvoidXfermode::Mode>(modeHandle);
-        return reinterpret_cast<jlong>(SkAvoidXfermode::Create(opColor, tolerance, mode));
+        AvoidXfermode::Mode mode = static_cast<AvoidXfermode::Mode>(modeHandle);
+        return reinterpret_cast<jlong>(AvoidXfermode::Create(opColor, tolerance, mode));
     }
 
     static jlong pixelxor_create(JNIEnv* env, jobject, jint opColor)
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 0bf269f..ef78fde 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -338,13 +338,15 @@
 }
 
 static jint
-android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address)
+android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name)
 {
     const char *c_address = env->GetStringUTFChars(device_address, NULL);
+    const char *c_name = env->GetStringUTFChars(device_name, NULL);
     int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device),
                                           static_cast <audio_policy_dev_state_t>(state),
-                                          c_address));
+                                          c_address, c_name));
     env->ReleaseStringUTFChars(device_address, c_address);
+    env->ReleaseStringUTFChars(device_name, c_name);
     return (jint) status;
 }
 
@@ -788,10 +790,11 @@
     jintArray jFormats = NULL;
     jobjectArray jGains = NULL;
     jobject jHandle = NULL;
+    jstring jDeviceName = NULL;
     bool useInMask;
 
-    ALOGV("convertAudioPortFromNative id %d role %d type %d",
-                                  nAudioPort->id, nAudioPort->role, nAudioPort->type);
+    ALOGV("convertAudioPortFromNative id %d role %d type %d name %s",
+        nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name);
 
     jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
     if (jSamplingRates == NULL) {
@@ -871,17 +874,21 @@
         goto exit;
     }
 
+    jDeviceName = env->NewStringUTF(nAudioPort->name);
+
     if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) {
         ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
         jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
         *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
-                                     jHandle, jSamplingRates, jChannelMasks, jFormats, jGains,
+                                     jHandle, jDeviceName,
+                                     jSamplingRates, jChannelMasks, jFormats, jGains,
                                      nAudioPort->ext.device.type, jAddress);
         env->DeleteLocalRef(jAddress);
     } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
         ALOGV("convertAudioPortFromNative is a mix");
         *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
-                                     jHandle, nAudioPort->role, jSamplingRates, jChannelMasks,
+                                     jHandle, nAudioPort->role, jDeviceName,
+                                     jSamplingRates, jChannelMasks,
                                      jFormats, jGains);
     } else {
         ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
@@ -905,6 +912,9 @@
     env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig);
 
 exit:
+    if (jDeviceName != NULL) {
+        env->DeleteLocalRef(jDeviceName);
+    }
     if (jSamplingRates != NULL) {
         env->DeleteLocalRef(jSamplingRates);
     }
@@ -1496,7 +1506,7 @@
     {"isStreamActiveRemotely","(II)Z",  (void *)android_media_AudioSystem_isStreamActiveRemotely},
     {"isSourceActive",      "(I)Z",     (void *)android_media_AudioSystem_isSourceActive},
     {"newAudioSessionId",   "()I",      (void *)android_media_AudioSystem_newAudioSessionId},
-    {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
+    {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
     {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
     {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
     {"setForceUse",         "(II)I",    (void *)android_media_AudioSystem_setForceUse},
@@ -1556,7 +1566,7 @@
     jclass audioPortClass = FindClassOrDie(env, "android/media/AudioPort");
     gAudioPortClass = MakeGlobalRefOrDie(env, audioPortClass);
     gAudioPortCstor = GetMethodIDOrDie(env, audioPortClass, "<init>",
-            "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+            "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V");
     gAudioPortFields.mHandle = GetFieldIDOrDie(env, audioPortClass, "mHandle",
                                                "Landroid/media/AudioHandle;");
     gAudioPortFields.mRole = GetFieldIDOrDie(env, audioPortClass, "mRole", "I");
@@ -1594,12 +1604,12 @@
     jclass audioDevicePortClass = FindClassOrDie(env, "android/media/AudioDevicePort");
     gAudioDevicePortClass = MakeGlobalRefOrDie(env, audioDevicePortClass);
     gAudioDevicePortCstor = GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
-            "(Landroid/media/AudioHandle;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+            "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
 
     jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
     gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
     gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>",
-            "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+            "(Landroid/media/AudioHandle;ILjava/lang/String;[I[I[I[Landroid/media/AudioGain;)V");
 
     jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain");
     gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 085cc81..6094fd1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -183,8 +183,6 @@
 
     <protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
     <protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
-    <protected-broadcast android:name="android.media.action.USB_AUDIO_ACCESSORY_PLUG" />
-    <protected-broadcast android:name="android.media.action.USB_AUDIO_DEVICE_PLUG" />
 
     <protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
     <protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
diff --git a/core/res/res/anim/disabled_anim_material.xml b/core/res/res/anim/disabled_anim_material.xml
deleted file mode 100644
index 6a7731e..0000000
--- a/core/res/res/anim/disabled_anim_material.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
-        <set>
-            <objectAnimator android:propertyName="alpha"
-                android:duration="@integer/disabled_alpha_animation_duration"
-                android:valueTo="?attr/disabledAlpha"
-                android:valueType="floatType"/>
-        </set>
-    </item>
-    <item>
-        <set>
-            <objectAnimator android:propertyName="alpha"
-                android:duration="@integer/disabled_alpha_animation_duration"
-                android:valueTo="1"
-                android:valueType="floatType"/>
-        </set>
-    </item>
-</selector>
\ No newline at end of file
diff --git a/core/res/res/color/btn_material_accent.xml b/core/res/res/color/btn_colored_material.xml
similarity index 93%
rename from core/res/res/color/btn_material_accent.xml
rename to core/res/res/color/btn_colored_material.xml
index 71469b6..b45f824 100644
--- a/core/res/res/color/btn_material_accent.xml
+++ b/core/res/res/color/btn_colored_material.xml
@@ -17,6 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
-          android:color="@color/button_material_dark" />
+          android:color="?attr/colorButtonNormal" />
     <item android:color="?attr/colorAccent" />
 </selector>
diff --git a/core/res/res/color/btn_material_accent.xml b/core/res/res/color/btn_colored_text_material.xml
similarity index 82%
copy from core/res/res/color/btn_material_accent.xml
copy to core/res/res/color/btn_colored_text_material.xml
index 71469b6..950d04a 100644
--- a/core/res/res/color/btn_material_accent.xml
+++ b/core/res/res/color/btn_colored_text_material.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -17,6 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
-          android:color="@color/button_material_dark" />
-    <item android:color="?attr/colorAccent" />
+          android:color="?attr/textColorSecondary" />
+    <item android:color="?attr/colorAccent"/>
 </selector>
diff --git a/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png
deleted file mode 100644
index d43e4d1..0000000
--- a/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png
deleted file mode 100644
index ddacb59..0000000
--- a/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png
deleted file mode 100644
index 21ed9144..0000000
--- a/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/ic_clear_material.xml b/core/res/res/drawable/ic_clear_material.xml
index 076c0a2..a21f47a 100644
--- a/core/res/res/drawable/ic_clear_material.xml
+++ b/core/res/res/drawable/ic_clear_material.xml
@@ -14,6 +14,13 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_clear_mtrl_alpha"
-    android:tint="?attr/colorControlNormal" />
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+<path
+    android:pathData="M19,6.41L17.59,5,12,10.59,6.41,5,5,6.41,10.59,12,5,17.59,6.41,19,12,13.41,17.59,19,19,17.59,13.41,12z"
+    android:fillColor="@color/white"/>
+</vector>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_material.xml b/core/res/res/drawable/scrubber_progress_horizontal_material.xml
index f2ea30f..89a1787 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_material.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_material.xml
@@ -14,29 +14,35 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@id/background">
         <nine-patch android:src="@drawable/scrubber_track_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item>
-        <layer-list>
-            <item android:id="@id/background">
-                <nine-patch android:src="@drawable/scrubber_track_mtrl_alpha"
                     android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:id="@id/secondaryProgress">
-                <scale android:scaleWidth="100%">
-                    <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha"
-                        android:tint="?attr/colorControlNormal" />
-                </scale>
-            </item>
-            <item android:id="@id/progress">
-                <scale android:scaleWidth="100%">
-                    <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha"
-                        android:tint="?attr/colorControlActivated" />
-                </scale>
-            </item>
-        </layer-list>
     </item>
-</selector>
+    <item android:id="@id/secondaryProgress">
+        <scale android:scaleWidth="100%">
+            <selector>
+                <item android:state_enabled="false">
+                    <color android:color="@color/transparent" />
+                </item>
+                <item>
+                    <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha"
+                                android:tint="?attr/colorControlNormal" />
+                </item>
+            </selector>
+        </scale>
+    </item>
+    <item android:id="@id/progress">
+        <scale android:scaleWidth="100%">
+            <selector>
+                <item android:state_enabled="false">
+                    <color android:color="@color/transparent" />
+                </item>
+                <item>
+                    <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha"
+                                android:tint="?attr/colorControlActivated" />
+                </item>
+            </selector>
+        </scale>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/layout/dialog_custom_title_material.xml b/core/res/res/layout/dialog_custom_title_material.xml
index 248a05e..50ed910 100644
--- a/core/res/res/layout/dialog_custom_title_material.xml
+++ b/core/res/res/layout/dialog_custom_title_material.xml
@@ -27,6 +27,8 @@
         android:layout_weight="0"
         android:gravity="center_vertical|start"
         style="?attr/windowTitleBackgroundStyle" />
+    <View android:layout_width="match_parent"
+        android:layout_height="0dp" />
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/core/res/res/layout/dialog_title_icons_material.xml b/core/res/res/layout/dialog_title_icons_material.xml
index 62af096..3866ca7 100644
--- a/core/res/res/layout/dialog_title_icons_material.xml
+++ b/core/res/res/layout/dialog_title_icons_material.xml
@@ -48,6 +48,9 @@
             android:layout_marginStart="8dip" />
     </LinearLayout>
 
+    <View android:layout_width="match_parent"
+        android:layout_height="0dp" />
+
     <FrameLayout
         android:layout_width="match_parent" android:layout_height="wrap_content"
         android:layout_weight="1"
diff --git a/core/res/res/layout/dialog_title_material.xml b/core/res/res/layout/dialog_title_material.xml
index 339d569..1ea7f6e 100644
--- a/core/res/res/layout/dialog_title_material.xml
+++ b/core/res/res/layout/dialog_title_material.xml
@@ -32,6 +32,8 @@
         android:paddingStart="?attr/dialogPreferredPadding"
         android:paddingEnd="?attr/dialogPreferredPadding"
         android:paddingTop="@dimen/dialog_padding_top_material" />
+    <View android:layout_width="match_parent"
+        android:layout_height="0dp" />
     <FrameLayout
         android:layout_width="match_parent" android:layout_height="wrap_content"
         android:layout_weight="1"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 3fdcaf7..ea22b15 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -16,12 +16,9 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="64dp"
-    internal:layout_minHeight="64dp"
-    internal:layout_maxHeight="64dp"
     >
     <include layout="@layout/notification_template_icon_group"
         android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 935424a..2a3ee90 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -16,12 +16,9 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    internal:layout_minHeight="65dp"
-    internal:layout_maxHeight="unbounded"
     >
     <include layout="@layout/notification_template_icon_group"
         android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 302e651..f1a9549 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -16,12 +16,9 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    internal:layout_minHeight="65dp"
-    internal:layout_maxHeight="unbounded"
     >
     <ImageView
         android:id="@+id/big_picture"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index d0c10b2..f657f04 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -16,12 +16,9 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    internal:layout_minHeight="65dp"
-    internal:layout_maxHeight="unbounded"
     >
     <include layout="@layout/notification_template_icon_group"
         android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index ac448ee..d292d4e 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -16,12 +16,9 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    internal:layout_minHeight="65dp"
-    internal:layout_maxHeight="unbounded"
     >
     <include layout="@layout/notification_template_icon_group"
         android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 69020a4..0292d28 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -16,14 +16,11 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="64dp"
     android:orientation="horizontal"
     android:background="#00000000"
-    internal:layout_minHeight="64dp"
-    internal:layout_maxHeight="64dp"
     >
     <include layout="@layout/notification_template_icon_group"
         android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/preference_dialog_edittext_material.xml b/core/res/res/layout/preference_dialog_edittext_material.xml
new file mode 100644
index 0000000..df76f7f
--- /dev/null
+++ b/core/res/res/layout/preference_dialog_edittext_material.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<!-- Layout used as the dialog's content View for EditTextPreference. -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="48dp"
+    android:layout_marginBottom="48dp"
+    android:overScrollMode="ifContentScrolls">
+
+    <LinearLayout
+        android:id="@+id/edittext_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="20dp"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_marginBottom="48dp"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:textColor="?attr/textColorSecondary"
+            style="?attr/textAppearanceSmall" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
new file mode 100644
index 0000000..92d4c1e
--- /dev/null
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+               android:transitionOrdering="together">
+     <transition class="com.android.internal.transition.EpicenterClipReveal"
+         android:interpolator="@android:interpolator/accelerate_cubic"
+         android:startDelay="50"
+         android:duration="300" />
+     <fade
+         android:interpolator="@android:interpolator/linear"
+         android:duration="100" />
+</transitionSet>
diff --git a/core/res/res/color/btn_material_accent.xml b/core/res/res/transition/popup_window_exit.xml
similarity index 70%
copy from core/res/res/color/btn_material_accent.xml
copy to core/res/res/transition/popup_window_exit.xml
index 71469b6..5cb9f0b 100644
--- a/core/res/res/color/btn_material_accent.xml
+++ b/core/res/res/transition/popup_window_exit.xml
@@ -13,10 +13,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/disabledAlpha"
-          android:color="@color/button_material_dark" />
-    <item android:color="?attr/colorAccent" />
-</selector>
+<fade xmlns:android="http://schemas.android.com/apk/res/android"
+      android:interpolator="@android:interpolator/linear" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 415e6d5..4eed141 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 uur gelede"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> uur gelede"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Afgelope <xliff:g id="COUNT">%d</xliff:g> dae"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Verlede maand"</string>
     <string name="older" msgid="5211975022815554840">"Ouer"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android begin tans …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimeer tans berging."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimeer program <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Begin programme."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Voltooi herlaai."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> loop"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kon nie aan Wi-Fikoppel nie"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" het \'n swak internetverbinding."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Laat verbinding toe?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s wil graag koppel aan %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"\'n Program"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Begin Wi-Fi Direct. Dit sal die Wi-Fi-kliënt/warmkol afskakel."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-versoek is gewysig tot DIAL-versoek."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-versoek is gewysig tot USSD-versoek."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-versoek is gewysig tot nuwe SS-versoek."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index aafffe8..036abe1 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"ከ1 ሰዓት በፊት"</item>
     <item quantity="other" msgid="2467273239587587569">"ከ <xliff:g id="COUNT">%d</xliff:g> ሰዓት በፊት"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"ቀኖች <xliff:g id="COUNT">%d</xliff:g> ያልቃል"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">" ያለፈው ወር"</string>
     <string name="older" msgid="5211975022815554840">"የድሮ"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android በመጀመር ላይ ነው…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ማከማቻን በማመቻቸት ላይ።"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"መተግበሪያዎች በአግባቡ በመጠቀም ላይ <xliff:g id="NUMBER_0">%1$d</xliff:g> ከ <xliff:g id="NUMBER_1">%2$d</xliff:g> ፡፡"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ን ማዘጋጀት።"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"መተግበሪያዎችን በማስጀመር ላይ፡፡"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"አጨራረስ ማስነሻ፡፡"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> አሂድ"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"ወደ Wi-Fi ለማያያዝ አልተቻለም"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ደካማ የበይነመረብ ግንኙነት ኣለው።"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"ግንኙነት ይፈቀድ?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ከ%2$s ጋር መገናኘት ይፈልጋል"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"መተግበሪያ %1$s ወደ Wifi Network %2$s መገናኘት ይፈልጋል"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"አንድ መተግበሪያ"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ቀጥታ"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"የWi-Fi በቀጥታ  ጀምር።ይህ የWi-Fi ደንበኛ /ድረስ ነጥብ  ያጠፋል።"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ጥያቄ ወደ ደውል ጥያቄ ተሻሽሎዋል።"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ጥያቄ ወደ USSD ጥያቄ ተሻሽሎዋል።"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ጥያቄ ወደ አዲስ SS ጥያቄ ተሻሽሎዋል።"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB Peripheral ወደብ"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 63340fc..2a4c65c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"قبل ساعة واحدة"</item>
     <item quantity="other" msgid="2467273239587587569">"قبل <xliff:g id="COUNT">%d</xliff:g> ساعات"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"آخر <xliff:g id="COUNT">%d</xliff:g> من الأيام"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"الشهر الماضي"</string>
     <string name="older" msgid="5211975022815554840">"أقدم"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"‏جارٍ تشغيل Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين السعة التخزينية."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"جارٍ تحسين التطبيق <xliff:g id="NUMBER_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"بدء التطبيقات."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"جارٍ إعادة التشغيل."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> يعمل"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"‏تعذر الاتصال بـ Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" لديها اتصال إنترنت رديء."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"هل تريد السماح بالاتصال؟"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"‏%1$s يرغب في الاتصال بـ %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"تطبيق"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"‏اتصال Wi-Fi مباشر"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"‏ابدأ Wi-Fi Direct. يؤدي هذا إلى إيقاف عميل/نقطة اتصال Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"‏يتم تعديل الطلب SS لطلب الاتصال."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"‏يتم تعديل طلب SS إلى طلب USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"‏يتم تعديل طلب SS إلى طلب SS الجديد."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2112d8a..0808af8 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Преди 1 час"</item>
     <item quantity="other" msgid="2467273239587587569">"Преди <xliff:g id="COUNT">%d</xliff:g> часа"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Последните <xliff:g id="COUNT">%d</xliff:g> дни"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Последният месец"</string>
     <string name="older" msgid="5211975022815554840">"По-стари"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android се стартира…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хранилището се оптимизира."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимизира се приложение <xliff:g id="NUMBER_0">%1$d</xliff:g> от <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Приложенията се стартират."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Зареждането завършва."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> се изпълнява"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не можа да се свърже с Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" има лоша връзка с интернет."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Да се разреши ли връзката?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s иска да установи връзка с/ъс %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Приложение"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Стартиране на Wi-Fi Direct. Това ще изключи клиентската програма/точката за достъп до Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS заявката е променена на DIAL заявка."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS заявката е променена на USSD заявка."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS заявката е променена на нова SS заявка."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index f37b7fb..bc61458 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"১ ঘন্টা আগে"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ঘন্টা আগে"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"গত <xliff:g id="COUNT">%d</xliff:g> দিনে"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"গত মাস"</string>
     <string name="older" msgid="5211975022815554840">"পুরোনো"</string>
   <plurals name="num_days_ago">
@@ -1259,7 +1257,7 @@
     <string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
     <string name="yes" msgid="5362982303337969312">"ঠিক আছে"</string>
     <string name="no" msgid="5141531044935541497">"বাতিল করুন"</string>
-    <string name="dialog_alert_title" msgid="2049658708609043103">"মনোযোগ"</string>
+    <string name="dialog_alert_title" msgid="2049658708609043103">"খেয়াল করুন"</string>
     <string name="loading" msgid="7933681260296021180">"লোড হচ্ছে..."</string>
     <string name="capital_on" msgid="1544682755514494298">"চালু করুন"</string>
     <string name="capital_off" msgid="6815870386972805832">"বন্ধ করুন"</string>
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android চালু হচ্ছে…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"সঞ্চয়স্থান অপ্টিমাইজ করা হচ্ছে৷"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>টির মধ্যে <xliff:g id="NUMBER_0">%1$d</xliff:g>টি অ্যাপ্লিকেশান অপ্টিমাইজ করা হচ্ছে৷"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> প্রস্তুত করা হচ্ছে৷"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"অ্যাপ্লিকেশানগুলি শুরু করা হচ্ছে৷"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"চালু করা সম্পূর্ণ হচ্ছে৷"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> চলছে"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi এর সাথে সংযোগ করা যায়নি"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" একটি দুর্বল ইন্টারনেট সংযোগ রয়েছে৷"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"সংযোগের মঞ্জুরি দেবেন?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s %2$s এর সাথে সংযোগ করতে চায়"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"অ্যাপ্লিকেশান %1$s Wifi নেটওয়ার্ক %2$s এর সাথে সংযোগ করতে চায়"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"একটি অ্যাপ্লিকেশান"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ডাইরেক্ট"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi ডাইরেক্ট আরম্ভ করুন৷ এটি Wi-Fi client/hotspot কে বন্ধ করবে৷"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS অনুরোধটিকে ডায়াল অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS অনুরোধটিকে নতুন USSD অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS অনুরোধটিকে নতুন SS অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB পেরিফেরাল পোর্ট"</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a2235fd..2a087b5 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Fa 1 hora"</item>
     <item quantity="other" msgid="2467273239587587569">"Fa <xliff:g id="COUNT">%d</xliff:g> hores"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Els darrers <xliff:g id="COUNT">%d</xliff:g> dies"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"El mes passat"</string>
     <string name="older" msgid="5211975022815554840">"Més antigues"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"S\'està iniciant Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"S\'està optimitzant l\'emmagatzematge."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"S\'està optimitzant l\'aplicació <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"S\'està preparant <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"S\'estan iniciant les aplicacions."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"S\'està finalitzant l\'actualització."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> s\'està executant"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"No s\'ha pogut connectar a la Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" té una mala connexió a Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vols permetre la connexió?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s vol connectar amb %2$s."</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"L\'aplicació %1$s vol connectar-se a la xarxa Wi-Fi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Una aplicació"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Inicia Wi-Fi Direct. Això desactivarà el client/la zona Wi-Fi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La sol·licitud SS s\'ha transformat en una sol·licitud DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La sol·licitud SS s\'ha transformat en una sol·licitud USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La sol·licitud SS s\'ha transformat en una sol·licitud SS nova."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Port perifèric USB"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index dc9eda2..8e485bd 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"před 1 h"</item>
     <item quantity="other" msgid="2467273239587587569">"před <xliff:g id="COUNT">%d</xliff:g> h"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Posledních <xliff:g id="COUNT">%d</xliff:g> dnů"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Poslední měsíc"</string>
     <string name="older" msgid="5211975022815554840">"Starší"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Spouštění systému Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Probíhá optimalizace úložiště."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimalizování aplikace <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Spouštění aplikací."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Dokončování inicializace."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Připojení k síti Wi-Fi se nezdařilo"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" má pomalé připojení k internetu."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Povolit připojení?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s se chce připojit k %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikace"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Přímé připojení sítě Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Spustit přímé připojení sítě Wi-Fi. Tato možnost vypne provoz sítě Wi-Fi v režimu klient/hotspot."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Požadavek SS byl změněn na požadavek DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Požadavek SS byl změněn na požadavek USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Požadavek SS byl změněn na nový požadavek SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1a412c1..bdb65a9 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"for 1 time siden"</item>
     <item quantity="other" msgid="2467273239587587569">"for <xliff:g id="COUNT">%d</xliff:g> timer siden"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Seneste <xliff:g id="COUNT">%d</xliff:g> dage"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Seneste måned"</string>
     <string name="older" msgid="5211975022815554840">"Ældre"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android starter..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lageret optimeres."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimerer app <xliff:g id="NUMBER_0">%1$d</xliff:g> ud af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Åbner dine apps."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Gennemfører start."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> er i gang"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kunne ikke oprette forbindelse til Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" har en dårlig internetforbindelse."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vil du tillade denne forbindelse?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s vil gerne oprette forbindelse til %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Applikationen %1$s vil gerne have forbindelse til Wi-Fi-netværk %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"En applikation"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. Dette slår Wi-Fi-klient/hotspot fra."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-anmodningen er ændret til en DIAL-anmodning."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-anmodningen er ændret til en USSD-anmodning."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-anmodningen er ændret til en ny SS-anmodning."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Ydre USB-port"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 56dd21e..14717c0 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"vor 1 Stunde"</item>
     <item quantity="other" msgid="2467273239587587569">"vor <xliff:g id="COUNT">%d</xliff:g> Stunden"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Letzte <xliff:g id="COUNT">%d</xliff:g> Tage"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Letzter Monat"</string>
     <string name="older" msgid="5211975022815554840">"Älter"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android wird gestartet…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Speicher wird optimiert"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> von <xliff:g id="NUMBER_1">%2$d</xliff:g> wird optimiert..."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Apps werden gestartet..."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Start wird abgeschlossen..."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Es konnte keine WLAN-Verbindung hergestellt werden."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" hat eine schlechte Internetverbindung."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Verbindung zulassen?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s möchte eine Verbindung mit %2$s herstellen."</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Eine App"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct-Betrieb starten. Hierdurch wird der WLAN-Client-/-Hotspot-Betrieb deaktiviert."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-Anfrage wird in DIAL-Anfrage geändert."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-Anfrage wird in USSD-Anfrage geändert."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-Anfrage wird in neue SS-Anfrage geändert."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 737cc1f..94d16cf 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -42,10 +42,10 @@
     <string name="untitled" msgid="4638956954852782576">"&lt;Χωρίς τίτλο&gt;"</string>
     <string name="emptyPhoneNumber" msgid="7694063042079676517">"(Δεν υπάρχει τηλεφωνικός αριθμός)"</string>
     <string name="unknownName" msgid="6867811765370350269">"Άγνωστο"</string>
-    <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Αυτόματος τηλεφωνητής"</string>
+    <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Αυτ/τος τηλεφωνητής"</string>
     <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
     <string name="mmiError" msgid="5154499457739052907">"Πρόβλημα σύνδεσης ή μη έγκυρος κώδικας MMI."</string>
-    <string name="mmiFdnError" msgid="5224398216385316471">"Η λειτουργία περιορίζεται μόνο σε καθορισμένους αριθμούς κλήσης."</string>
+    <string name="mmiFdnError" msgid="5224398216385316471">"Η λειτουργία περιορίζεται μόνο σε προκαθορισμένους αριθμούς κλήσης."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"Η υπηρεσία ενεργοποιήθηκε."</string>
     <string name="serviceEnabledFor" msgid="6856228140453471041">"Η υπηρεσία ενεργοποιήθηκε για:"</string>
     <string name="serviceDisabled" msgid="1937553226592516411">"Η υπηρεσία έχει απενεργοποιηθεί."</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"πριν από 1 ώρα"</item>
     <item quantity="other" msgid="2467273239587587569">"πριν από <xliff:g id="COUNT">%d</xliff:g> ώρες"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Τελευταίες <xliff:g id="COUNT">%d</xliff:g> ημέρες"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Τελευταίος μήνας"</string>
     <string name="older" msgid="5211975022815554840">"Παλαιότερα"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Εκκίνηση Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Βελτιστοποίηση αποθηκευτικού χώρου."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Βελτιστοποίηση της εφαρμογής <xliff:g id="NUMBER_0">%1$d</xliff:g> από <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Προετοιμασία <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Έναρξη εφαρμογών."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Ολοκλήρωση εκκίνησης."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Δεν είναι δυνατή η σύνδεση στο Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" έχει κακή σύνδεση στο Διαδίκτυο."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Να επιτρέπεται η σύνδεση;"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s θα ήθελε να συνδεθεί με %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Η εφαρμογή %1$s θα ήθελε να συνδεθεί με το δίκτυο WiFi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Μια εφαρμογή"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Ξεκινήστε τη λειτουργία Wi-Fi Direct. Θα απενεργοποιηθεί η λειτουργία πελάτη/φορητού σημείου πρόσβασης Wi-Fi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Το αίτημα SS τροποποιήθηκε σε αίτημα DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Το αίτημα SS τροποποιήθηκε σε αίτημα USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Το αίτημα SS τροποποιήθηκε σε νέο αίτημα SS."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Περιφερειακή θύρα USB"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index df8b41f..643e190 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 hour ago"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> hours ago"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Last <xliff:g id="COUNT">%d</xliff:g> days"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Last month"</string>
     <string name="older" msgid="5211975022815554840">"Older"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android is starting…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimising app <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starting apps."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Finishing boot."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Couldn\'t connect to Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" has a poor Internet connection."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Allow connection?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s would like to connect to %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Application %1$s would like to connect to Wi-Fi Network %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"An application"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS request is modified to DIAL request."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS request is modified to USSD request."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS request is modified to new SS request."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB Peripheral Port"</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index df8b41f..643e190 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 hour ago"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> hours ago"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Last <xliff:g id="COUNT">%d</xliff:g> days"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Last month"</string>
     <string name="older" msgid="5211975022815554840">"Older"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android is starting…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimising storage."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimising app <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starting apps."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Finishing boot."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Couldn\'t connect to Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" has a poor Internet connection."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Allow connection?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s would like to connect to %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Application %1$s would like to connect to Wi-Fi Network %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"An application"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS request is modified to DIAL request."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS request is modified to USSD request."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS request is modified to new SS request."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB Peripheral Port"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a9353db..a35ad8d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1139,17 +1139,15 @@
     <item quantity="other" msgid="2176942008915455116">"hace <xliff:g id="COUNT">%d</xliff:g> minutos"</item>
   </plurals>
   <plurals name="num_hours_ago">
-    <item quantity="one" msgid="9150797944610821849">"Hace 1 hora."</item>
-    <item quantity="other" msgid="2467273239587587569">"Hace <xliff:g id="COUNT">%d</xliff:g> horas."</item>
+    <item quantity="one" msgid="9150797944610821849">"Hace 1 hora"</item>
+    <item quantity="other" msgid="2467273239587587569">"Hace <xliff:g id="COUNT">%d</xliff:g> horas"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> días"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Último mes"</string>
     <string name="older" msgid="5211975022815554840">"Antiguos"</string>
   <plurals name="num_days_ago">
     <item quantity="one" msgid="861358534398115820">"ayer"</item>
-    <item quantity="other" msgid="2479586466153314633">"Hace <xliff:g id="COUNT">%d</xliff:g> días."</item>
+    <item quantity="other" msgid="2479586466153314633">"Hace <xliff:g id="COUNT">%d</xliff:g> días"</item>
   </plurals>
   <plurals name="in_num_seconds">
     <item quantity="one" msgid="2729745560954905102">"en 1 segundo"</item>
@@ -1176,7 +1174,7 @@
     <item quantity="other" msgid="851164968597150710">"Hace <xliff:g id="COUNT">%d</xliff:g> minutos."</item>
   </plurals>
   <plurals name="abbrev_num_hours_ago">
-    <item quantity="one" msgid="4796212039724722116">"Hace 1 hora."</item>
+    <item quantity="one" msgid="4796212039724722116">"Hace 1 hora"</item>
     <item quantity="other" msgid="6889970745748538901">"hace <xliff:g id="COUNT">%d</xliff:g> horas"</item>
   </plurals>
   <plurals name="abbrev_num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Iniciando Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizando la aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando aplicaciones"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Finalizando el inicio"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"No se pudo conectar a la red Wi-Fi."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tiene una mala conexión a Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"¿Permitir la conexión?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s quiere conectarse a %2$s."</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Una aplicación"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar Wi-Fi Direct. Se desactivará el funcionamiento de la zona o del cliente Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La solicitud SS cambió por una solicitud DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La solicitud SS cambió por una solicitud USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La solicitud SS cambió por una nueva solicitud SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 96856c0..d56a893 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Hace 1 hora"</item>
     <item quantity="other" msgid="2467273239587587569">"Hace <xliff:g id="COUNT">%d</xliff:g> horas"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> días"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"El mes pasado"</string>
     <string name="older" msgid="5211975022815554840">"Anterior"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android se está iniciando…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamiento."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizando aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>..."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando aplicaciones"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Finalizando inicio..."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"No se ha podido establecer conexión con la red Wi-Fi."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tiene una conexión inestable a Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"¿Permitir la conexión?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s quiere conectarse a %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Una aplicación"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar Wi-Fi Direct. Se desactivará el funcionamiento de la zona o del cliente Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La solicitud SS se ha modificado para la solicitud DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La solicitud SS se ha modificado para la solicitud USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La solicitud SS se ha modificado para la nueva solicitud SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index ccb7b9f..eec356a 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 tund tagasi"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> tundi tagasi"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Viimased <xliff:g id="COUNT">%d</xliff:g> päeva"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Eelmisel kuul"</string>
     <string name="older" msgid="5211975022815554840">"Vanem"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android käivitub ..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Salvestusruumi optimeerimine."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g>. rakenduse <xliff:g id="NUMBER_1">%2$d</xliff:g>-st optimeerimine."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> ettevalmistamine."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Rakenduste käivitamine."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Käivitamise lõpuleviimine."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> töötab"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ei saanud WiFi-ga ühendust"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" on halb Interneti-ühendus."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Kas lubada ühendus?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s soovib luua ühenduse võrguga %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Rakendus %1$s soovib luua ühenduse WiFi-võrguga %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Rakendus"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"WiFi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käivitage WiFi otseühendus. See lülitab välja WiFi kliendi/leviala."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-päring muudeti DIAL-päringuks."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-päring muudeti USSD-päringuks."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-päring muudeti uueks SS-päringuks."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Väline USB-port"</string>
 </resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 51e9fee..bf2b274 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -424,7 +424,7 @@
     <string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"Lotu ahots bidezko elkarrekintzako zerbitzuei"</string>
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Ahots bidezko elkarrekintzako zerbitzu baten goi-mailako interfazeari lotzea baimentzen die titularrei. Aplikazio normalek ez lukete beharko."</string>
     <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"kudeatu ahozko gako-esaldiak"</string>
-    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Ahoz esandako gako-hitzak hautemateko gako-esaldiak kudeatzea baimentzen die aplikazioei. Aplikazio normalek ez lukete behar."</string>
+    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Ahozko pasahitzak hautemateko gako-esaldiak kudeatzea baimentzen die aplikazioei. Aplikazio normalek ez lukete behar."</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"Lotu urruneko pantaila batera"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Urruneko pantaila baten goi-mailako interfazera lotzeko aukera ematen dio titularrari. Aplikazio normalek ez dute baimen hau behar."</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"lotu widget-zerbitzu batekin"</string>
@@ -559,7 +559,7 @@
     <string name="permdesc_controlVpn" msgid="762852603315861214">"Sare pribatu birtualen behe-mailako eginbideak kontrolatzea baimentzen die aplikazioei."</string>
     <string name="permlab_captureAudioOutput" msgid="6857134498402346708">"Grabatu audio-irteera"</string>
     <string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Audio-irteera grabatzeko eta birbideratzeko aukera ematen die aplikazioei."</string>
-    <string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Hauteman Hotword"</string>
+    <string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Hauteman ahozko pasahitza"</string>
     <string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Hotword bidez hauteman daitekeen audioa grabatzeko aukera ematen die aplikazioei. Atzeko planoan grabatzeak ez du bestelako audio-grabazioak (adibidez, bideokamera bidezkoak) egitea eragozten."</string>
     <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Audio-bideratzea"</string>
     <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Audio-bideratzea zuzenean kontrolatzea eta audio-gidalerroei gainjartzea baimentzen die aplikazioei."</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Duela ordubete"</item>
     <item quantity="other" msgid="2467273239587587569">"duela <xliff:g id="COUNT">%d</xliff:g> ordu"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"azken <xliff:g id="COUNT">%d</xliff:g> egunak"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Azken hilabetea"</string>
     <string name="older" msgid="5211975022815554840">"Zaharragoa"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android abiarazten ari da…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Memoria optimizatzen."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> aplikazio optimizatzen."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> prestatzen."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Aplikazioak abiarazten."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Bertsio-berritzea amaitzen."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> exekutatzen"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ezin izan da Wi-Fi sarera konektatu"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" Interneteko konexio txarra du."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Konektatzea baimendu nahi diozu?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s aplikazioak %2$s sarera konektatu nahi du"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s aplikazioak %2$s Wi-Fi sarera konektatu nahi du"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikazio bat"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Hasi Wi-Fi Direct. Wi-Fi bezeroa edo sare publikoa desaktibatuko da."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS eskaera DIAL eskaerara aldatu da."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS eskaera USSD eskaerara aldatu da."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS eskaera SS eskaera berrira aldatu da."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB ataka periferikoa"</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b04d53c..7a71ceb 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"۱ ساعت قبل"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ساعت قبل"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> روز گذشته"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"ماه گذشته"</string>
     <string name="older" msgid="5211975022815554840">"قدیمی تر"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"‏Android در حال راه‌اندازی است..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"بهینه‌سازی فضای ذخیره‌سازی."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"در حال بهینه‌سازی برنامهٔ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"در حال آغاز برنامه‌ها."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"در حال اتمام راه‌اندازی."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> در حال اجرا"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"‏اتصال به Wi-Fi ممکن نیست"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" اتصال اینترنتی ضعیفی دارد."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"اتصال اجازه داده شود؟"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"‏%1$s می‌خواهد به %2$s متصل شود"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"برنامه"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"‏Wi-Fi Direct را شروع کنید. این کار نقطه اتصال/سرویس گیرنده Wi-Fi را غیرفعال خواهد کرد."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"‏درخواست SS به درخواست DIAL اصلاح می‌شود."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"‏درخواست SS به درخواست USSD اصلاح می‌شود."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"‏درخواست SS به درخواست SS جدید اصلاح می‌شود."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 49dd96f..feb93d9 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 tunti sitten"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> tuntia sitten"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Viimeisen <xliff:g id="COUNT">%d</xliff:g> päivän aikana"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Viime kuussa"</string>
     <string name="older" msgid="5211975022815554840">"Vanhemmat"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android käynnistyy…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimoidaan tallennustilaa."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimoidaan sovellusta <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Valmistellaan: <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Käynnistetään sovelluksia."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Viimeistellään päivitystä."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> käynnissä"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi-yhteyden muodostaminen epäonnistui"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" : huono internetyhteys."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Sallitaanko yhteys?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s haluaa yhdistää kohteeseen %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Sovellus %1$s yrittää yhdistää Wi-Fi-verkkoon %2$s."</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Sovellus"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Suora Wi-Fi-yhteys"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käynnistä suora Wi-Fi-yhteys. Wi-Fi-asiakas/-hotspot poistetaan käytöstä."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-pyyntö muutettiin DIAL-pyynnöksi."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-pyyntö muutettiin USSD-pyynnöksi."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-pyyntö muutettiin uudeksi SS-pyynnöksi."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB-oheislaiteportti"</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index df8d9c1..a71d616 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Il y a 1 heure"</item>
     <item quantity="other" msgid="2467273239587587569">"il y a <xliff:g id="COUNT">%d</xliff:g> heures"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Les <xliff:g id="COUNT">%d</xliff:g> derniers jours"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Le mois dernier"</string>
     <string name="older" msgid="5211975022815554840">"Précédent"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android en cours de démarrage..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimisation de l\'application <xliff:g id="NUMBER_0">%1$d</xliff:g> sur <xliff:g id="NUMBER_1">%2$d</xliff:g>…"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Lancement des applications…"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Finalisation de la mise à jour."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Impossible de se connecter au Wi-Fi."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" dispose d\'une mauvaise connexion Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Autoriser la connexion?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s souhaite se connecter à %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Une application"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Lancer le Wi-Fi Direct. Cela désactive le fonctionnement du Wi-Fi client ou via un point d\'accès."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La demande SS a été modifiée et est maintenant une demande DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La demande SS a été modifiée et est maintenant une demande USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La demande SS a été modifiée et est maintenant une nouvelle demande SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 1945e78..83df48d 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"il y a 1 heure"</item>
     <item quantity="other" msgid="2467273239587587569">"Il y a <xliff:g id="COUNT">%d</xliff:g> heures"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Les <xliff:g id="COUNT">%d</xliff:g> derniers jours"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Le mois dernier"</string>
     <string name="older" msgid="5211975022815554840">"Préc."</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Démarrage d\'Android en cours"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimisation du stockage en cours…"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimisation de l\'application <xliff:g id="NUMBER_0">%1$d</xliff:g> sur <xliff:g id="NUMBER_1">%2$d</xliff:g>…"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Lancement des applications…"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Finalisation de la mise à jour."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Impossible de se connecter au Wi-Fi."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" dispose d\'une mauvaise connexion Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Autoriser la connexion ?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s souhaite se connecter à %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Une application"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Lancer le Wi-Fi Direct. Cela désactive le fonctionnement du Wi-Fi client ou via un point d\'accès."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La requête SS a été remplacée par une requête DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La requête SS a été remplacée par une requête USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La requête SS a été remplacée par une autre requête SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 78d223c..b6122b4 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Hai 1 hora"</item>
     <item quantity="other" msgid="2467273239587587569">"hai <xliff:g id="COUNT">%d</xliff:g> horas"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> días"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"O mes pasado"</string>
     <string name="older" msgid="5211975022815554840">"Antes"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Estase iniciando Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizando almacenamento."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizando aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando aplicacións."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Está finalizando o arranque"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> está en execución"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Non se puido conectar coa rede Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ten unha conexión a Internet deficiente."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Queres permitir a conexión?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s quere conectarse a %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"A aplicación %1$s quere conectarse á rede wifi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Unha aplicación"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Inicia Wi-Fi Direct. Esta acción desactivará o cliente e a zona interactiva da wifi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"A solicitude SS transformouse nunha solicitude DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"A solicitude SS transformouse nunha solicitude USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"A solicitude SS transformouse nunha nova solicitude SS."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Porto periférico USB"</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d17a5cb..64f0ab8 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -71,7 +71,7 @@
     <string name="ClirMmi" msgid="7784673673446833091">"आउटगोइंग कॉलर ID"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"कनेक्ट किया गया लाइन आईडी"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"कनेक्ट किया गया लाइन आईडी प्रतिबंध"</string>
-    <string name="CfMmi" msgid="5123218989141573515">"कॉल अग्रेषण"</string>
+    <string name="CfMmi" msgid="5123218989141573515">"कॉल आगे भेजना"</string>
     <string name="CwMmi" msgid="9129678056795016867">"कॉल प्रतीक्षा"</string>
     <string name="BaMmi" msgid="455193067926770581">"कॉल बाधित करना"</string>
     <string name="PwdMmi" msgid="7043715687905254199">"पासवर्ड बदलें"</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 घंटे पहले"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> घंटे पहले"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"अंतिम <xliff:g id="COUNT">%d</xliff:g> दिन"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"पिछला माह"</string>
     <string name="older" msgid="5211975022815554840">"इससे पुराना"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android प्रारंभ हो रहा है…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"मेमोरी ऑप्‍टिमाइज़ हो रही है."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> में से <xliff:g id="NUMBER_0">%1$d</xliff:g> ऐप्स  अनुकूलित हो रहा है."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तैयार हो रहा है."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"ऐप्स  प्रारंभ होने वाले हैं"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"बूट समाप्‍त हो रहा है."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> चल रही है"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"वाई-फ़ाई  से कनेक्‍ट नहीं हो सका"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" के पास एक कमज़ोर इंटरनेट कनेक्‍शन है."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"कनेक्शन की अनुमति दें?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s, %2$s से कनेक्ट होना चाहता/चाहती है"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s ऐप्‍लिकेशन %2$s वाई-फ़ाई नेटवर्क से कनेक्‍ट करना चाहता है"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"ऐप्लिकेशन"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"वाई-फ़ाई डायरेक्ट"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"वाई-फ़ाई  डायरेक्ट प्रारंभ करें. इससे वाई-फ़ाई  क्‍लाइंट/हॉटस्पॉट कार्यवाही बंद हो जाएगी."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS अनुरोध को DIAL अनुरोध में बदल दिया गया है."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS अनुरोध को USSD अनुरोध में बदल दिया गया है."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS अनुरोध को नए SS अनुरोध में बदल दिया गया है."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB पेरिफ़ेरल पोर्ट"</string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 21c9938..d6c9660 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Prije 1 sata"</item>
     <item quantity="other" msgid="2467273239587587569">"Prije <xliff:g id="COUNT">%d</xliff:g> h"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Posljednjih ovoliko dana: <xliff:g id="COUNT">%d</xliff:g>"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Prošli mjesec"</string>
     <string name="older" msgid="5211975022815554840">"Starije"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Pokretanje Androida..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje pohrane."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimiziranje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Pokretanje aplikacija."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Završetak inicijalizacije."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Izvodi se <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ne može se spojiti na Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ima lošu internetsku vezu."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Dopustiti povezivanje?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s traži povezivanje sa sljedećim: %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikacija"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Izravni Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Pokreni izravan rad s Wi-Fi mrežom. To će isključiti rad s Wi-Fi klijentom/žarišnom točkom."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS zahtjev izmijenjen je u DIAL zahtjev."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS zahtjev izmijenjen je u USSD zahtjev."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS zahtjev izmijenjen je u novi SS zahtjev."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 978d115..ec5e47e 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -56,7 +56,7 @@
     <string name="badPin" msgid="9015277645546710014">"A megadott régi PIN kód helytelen."</string>
     <string name="badPuk" msgid="5487257647081132201">"A megadott PUK kód helytelen."</string>
     <string name="mismatchPin" msgid="609379054496863419">"A beírt PIN kódok nem egyeznek."</string>
-    <string name="invalidPin" msgid="3850018445187475377">"Írjon be egy 4-8 számjegyű PIN-kódot."</string>
+    <string name="invalidPin" msgid="3850018445187475377">"Írjon be egy 4-8 számjegyű PIN kódot."</string>
     <string name="invalidPuk" msgid="8761456210898036513">"8 számjegyű vagy hosszabb PUK kódot írjon be."</string>
     <string name="needPuk" msgid="919668385956251611">"A SIM kártya le van zárva a PUK kóddal. A feloldáshoz adja meg a PUK kódot."</string>
     <string name="needPuk2" msgid="4526033371987193070">"A SIM kártya feloldásához adja meg a PUK2 kódot."</string>
@@ -75,7 +75,7 @@
     <string name="CwMmi" msgid="9129678056795016867">"Hívásvárakoztatás"</string>
     <string name="BaMmi" msgid="455193067926770581">"Hívásletiltás"</string>
     <string name="PwdMmi" msgid="7043715687905254199">"Jelszómódosítás"</string>
-    <string name="PinMmi" msgid="3113117780361190304">"PIN-kód módosítása"</string>
+    <string name="PinMmi" msgid="3113117780361190304">"PIN kód módosítása"</string>
     <string name="CnipMmi" msgid="3110534680557857162">"Szám hívása"</string>
     <string name="CnirMmi" msgid="3062102121430548731">"Hívószám korlátozva"</string>
     <string name="ThreeWCMmi" msgid="9051047170321190368">"Háromutas hívás"</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 órája"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> órája"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Elmúlt <xliff:g id="COUNT">%d</xliff:g> napban"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Múlt hónapban"</string>
     <string name="older" msgid="5211975022815554840">"Régebbi"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Az Android indítása…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tárhely-optimalizálás."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Alkalmazás optimalizálása: <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> előkészítése."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Kezdő alkalmazások."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Rendszerindítás befejezése."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> fut"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nem sikerült csatlakozni a Wi-Fi hálózathoz"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" rossz internetkapcsolattal rendelkezik."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Engedélyezi a csatlakozást?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"A(z)%1$s szeretne csatlakozni a következőhöz: %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"A(z) %1$s alkalmazás szeretne csatlakozni a következő Wi-Fi hálózathoz: %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Egy alkalmazás"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct elindítása. A Wi-Fi kliens/hotspot ettől leáll."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Az SS-kérés módosítva DIAL-kérésre."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Az SS-kérés módosítva USSD-kérésre."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Az SS-kérés módosítva új SS-kérésre."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB-perifériaport"</string>
 </resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 101a726..5e2c5a9 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -22,8 +22,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="byteShort" msgid="8340973892742019101">"Բ"</string>
     <string name="kilobyteShort" msgid="5973789783504771878">"Կբ"</string>
-    <string name="megabyteShort" msgid="6355851576770428922">"Մբ"</string>
-    <string name="gigabyteShort" msgid="3259882455212193214">"Գբ"</string>
+    <string name="megabyteShort" msgid="6355851576770428922">"ՄԲ"</string>
+    <string name="gigabyteShort" msgid="3259882455212193214">"ԳԲ"</string>
     <string name="terabyteShort" msgid="231613018159186962">"Տբ"</string>
     <string name="petabyteShort" msgid="5637816680144990219">"Պբ"</string>
     <string name="fileSizeSuffix" msgid="9164292791500531949">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 ժամ առաջ"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ժամ առաջ"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Վերջին <xliff:g id="COUNT">%d</xliff:g> օրերին"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Անցյալ ամիս"</string>
     <string name="older" msgid="5211975022815554840">"Ավելի հին"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android-ը մեկնարկում է…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Պահեստի օպտիմալացում:"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Օպտիմալացվում է հավելված <xliff:g id="NUMBER_0">%1$d</xliff:g>-ը <xliff:g id="NUMBER_1">%2$d</xliff:g>-ից:"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը պատրաստվում է:"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Հավելվածները մեկնարկում են:"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Բեռնումն ավարտվում է:"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>-ն աշխատում է"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Չհաջողվեց միանալ Wi-Fi-ին"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ունի թույլ ինտերնետ կապ:"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Թույլատրե՞լ կապը:"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s օգտվողը ցանկանում է կապվել %2$s-ին"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s հավելվածը ցանկանում է միանալ %2$s Wifi ցանցին"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Հավելված"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ուղիղ"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Մեկնարկել Wi-Fi ուղին: Այն կանջատի Wi-Fi հաճախորդ/թեժ կետ գործողությունը:"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS հարցումը փոխվել է DIAL հարցման:"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS հարցումը փոխվել է USSD հարցման:"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS հարցումը փոխվել է նոր SS հարցման:"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB արտաքին միացք"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 085939a..70bcb1b 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 jam yang lalu"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> jam yang lalu"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> hari terakhir"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Bulan lalu"</string>
     <string name="older" msgid="5211975022815554840">"Lawas"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Memulai Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimalkan penyimpanan."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Mengoptimalkan aplikasi <xliff:g id="NUMBER_0">%1$d</xliff:g> dari <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Menyiapkan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Memulai aplikasi."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Menyelesaikan boot."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> berjalan"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Tidak dapat tersambung ke Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" memiliki sambungan internet yang buruk."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Izinkan hubungan?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ingin terhubung ke %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Aplikasi %1$s ingin tersambung ke Jaringan Wifi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikasi"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Memulai Wi-Fi Direct. Opsi ini akan mematikan hotspot/klien Wi-Fi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Permintaan SS diubah menjadi permintaan DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Permintaan SS diubah menjadi permintaan USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Permintaan SS diubah menjadi permintaan SS baru."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Port Periferal USB"</string>
 </resources>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 2839911..1d7aaba 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"fyrir klukkustund"</item>
     <item quantity="other" msgid="2467273239587587569">"fyrir <xliff:g id="COUNT">%d</xliff:g> klukkustundum"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Síðustu <xliff:g id="COUNT">%d</xliff:g> daga"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Í síðasta mánuði"</string>
     <string name="older" msgid="5211975022815554840">"Eldra"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android er að ræsast…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Fínstillir geymslu."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Fínstillir forrit <xliff:g id="NUMBER_0">%1$d</xliff:g> af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Undirbýr <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Ræsir forrit."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Lýkur ræsingu."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> er í gangi"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ekki var hægt að tengjast Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" er með lélegt netsamband."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Leyfa tengingu?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s vill fá að tengjast %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Forritið %1$s vill tengjast Wi-Fi netinu %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Forrit"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Ræsa Wi-Fi Direct. Þetta mun slökkva á Wi-Fi biðlara/aðgangsstað."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-beiðni er breytt í DIAL-beiðni."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-beiðni er breytt í USSD-beiðni."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-beiðni er breytt í nýja SS-beiðni."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB-tengi fyrir jaðartæki"</string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index e1f1d7c..6443c1a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 ora fa"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ore fa"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Ultimi <xliff:g id="COUNT">%d</xliff:g> giorni"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Ultimo mese"</string>
     <string name="older" msgid="5211975022815554840">"Precedente"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Avvio di Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ottimizzazione archiviazione."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Ottimizzazione applicazione <xliff:g id="NUMBER_0">%1$d</xliff:g> di <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Avvio applicazioni."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Conclusione dell\'avvio."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> in esecuzione"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Impossibile connettersi alla rete Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ha una connessione Internet debole."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Consentire la connessione?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s vorrebbe connettersi a %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Un\'applicazione"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Avvia Wi-Fi Direct. Verrà disattivato il client/hotspot Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La richiesta SS è stata modificata in richiesta DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La richiesta SS è stata modificata in richiesta USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La richiesta SS è stata modificata in nuova richiesta SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 196a774..3c5390a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"לפני שעה"</item>
     <item quantity="other" msgid="2467273239587587569">"לפני <xliff:g id="COUNT">%d</xliff:g> שעות"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> הימים האחרונים"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"בחודש שעבר"</string>
     <string name="older" msgid="5211975022815554840">"ישן יותר"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"‏הפעלת Android מתחילה…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"מתבצעת אופטימיזציה של האחסון."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"מבצע אופטימיזציה של אפליקציה <xliff:g id="NUMBER_0">%1$d</xliff:g> מתוך <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"מפעיל אפליקציות."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"מסיים אתחול."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> פועל"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"‏אין אפשרות להתחבר ל-Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" אינו מחובר היטב לאינטרנט."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"האם להתיר את החיבור?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"‏%1$s רוצה להתחבר אל %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"אפליקציה"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"‏Wi-Fi ישיר"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"‏הפעל Wi-Fi ישיר. פעולה זו תכבה את הנקודה לשיתוף אינטרנט ב-Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"‏בקשת SS שונתה לבקשת DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"‏בקשת SS שונתה לבקשת USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"‏בקשת SS שונתה לבקשת SS חדשה."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 67b72d4..3109a9b 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1時間前"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g>時間前"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"過去<xliff:g id="COUNT">%d</xliff:g>日間"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"先月"</string>
     <string name="older" msgid="5211975022815554840">"もっと前"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Androidの起動中…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ストレージを最適化しています。"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>個中<xliff:g id="NUMBER_0">%1$d</xliff:g>個のアプリを最適化しています。"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"アプリを起動しています。"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"ブートを終了しています。"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>を実行中"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fiに接続できませんでした"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" はインターネット接続に問題があります。"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"接続を許可しますか?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$sさんが「%2$s」への接続を希望しています"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"アプリ"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Directを開始します。これによりWi-Fiクライアント/アクセスポイントがOFFになります。"</string>
@@ -1418,7 +1419,7 @@
     <string name="usb_ptp_notification_title" msgid="1960817192216064833">"カメラとして接続"</string>
     <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"インストーラとして接続"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBアクセサリを接続しました"</string>
-    <string name="usb_notification_message" msgid="2290859399983720271">"他のUSBオプションをタップしてください。"</string>
+    <string name="usb_notification_message" msgid="2290859399983720271">"USB接続方法を変更するにはタップしてください。"</string>
     <string name="extmedia_format_title" product="nosdcard" msgid="9020092196061007262">"USBストレージをフォーマットしますか?"</string>
     <string name="extmedia_format_title" product="default" msgid="3648415921526526069">"SDカードをフォーマットしますか?"</string>
     <string name="extmedia_format_message" product="nosdcard" msgid="3934016853425761078">"USBストレージに保存されているファイルはすべて消去されます。この操作は元に戻せません。"</string>
@@ -1847,7 +1848,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"画面固定を解除する前にロック解除パターンの入力を求める"</string>
     <string name="lock_to_app_unlock_password" msgid="6380979775916974414">"オフライン再生を解除する前にパスワードの入力を求める"</string>
     <string name="battery_saver_description" msgid="1960431123816253034">"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使用するその他のアプリは、起動しても更新されないことがあります。\n\nバッテリーセーバーは端末の充電中は自動的にOFFになります。"</string>
-    <string name="downtime_condition_summary" msgid="8761776337475705749">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>にダウンロードが終わるまで"</string>
+    <string name="downtime_condition_summary" msgid="8761776337475705749">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>にダウンタイムが終わるまで"</string>
     <string name="downtime_condition_line_one" msgid="8762708714645352010">"ダウンタイム終了まで"</string>
   <plurals name="zen_mode_duration_minutes_summary">
     <item quantity="one" msgid="3177683545388923234">"1分間(<xliff:g id="FORMATTEDTIME">%2$s</xliff:g>まで)"</item>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SSリクエストはDIALリクエストに変更されました。"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SSリクエストはUSSDリクエストに変更されました。"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SSリクエストは新しいSSリクエストに変更されました。"</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 9ab84bef4..3c84acb 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 საათის წინ"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> საათის წინ"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"ბოლო <xliff:g id="COUNT">%d</xliff:g> დღე"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"გასული თვე"</string>
     <string name="older" msgid="5211975022815554840">"უფრო ძველი"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android იწყება…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"მეხსიერების ოპტიმიზირება."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"მიმდინარეობს აპლიკაციების ოპტიმიზაცია. დასრულებულია <xliff:g id="NUMBER_0">%1$d</xliff:g>, სულ <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"ემზადება <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"აპების ჩართვა"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"ჩატვირთვის დასასრული."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi-თან დაკავშირება ვერ მოხერხდა"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" აქვს ცუდი ინტერნეტ კავშირი."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"გსურთ კავშირის დაშვება?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s-ს სურს %2$s-თან დაკავშირება"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"განაცხადს %1$s სურს დაუკავშირდეს Wifi ქსელს %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"აპლიკაცია"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"ჩართეთ Wi-Fi Direct. ეს გამოიწვევს Wi-Fi კლიენტისა/უსადენო ქსელის გამორთვას."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS მოთხოვნა შეიცვალა DIAL მოთხოვნით."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS მოთხოვნა შეიცვალა USSD მოთხოვნით."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS მოთხოვნა შეიცვალა ახალი SS მოთხოვნით."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"პერიფერიული USB პორტი"</string>
 </resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 255fedc..f8df80c 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 сағат бұрын"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> сағат бұрын"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Соңғы <xliff:g id="COUNT">%d</xliff:g> күнде"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Соңғы ай"</string>
     <string name="older" msgid="5211975022815554840">"Ескілеу"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android іске қосылуда…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Қойманы оңтайландыру."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ішінен <xliff:g id="NUMBER_0">%1$d</xliff:g> қолданба оңтайландырылуда."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> дайындалуда."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Қолданбалар іске қосылуда."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Қосуды аяқтауда."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> қосылған"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi желісіне қосыла алмады"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" Интернет байланысы нашар."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Қосылуға рұқсат ету керек пе?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s %2$s қосылғысы келеді"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s қолданбасы %2$s Wi-Fi желісіне қосылғысы келеді"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Қолданба"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi тікелей"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Тікелей байланысын бастау. Бұл Wi-Fi клиент/хот-спотты өшіреді."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS сұрауы DIAL сұрауына өзгертілді."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS сұрауы USSD сұрауына өзгертілді."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS сұрауы жаңа SS сұрауына өзгертілді."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB перифериялық порты"</string>
 </resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index f8e88f5..2e973dc 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"១ ម៉ោង​មុន"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ម៉ោង​មុន"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> ថ្ងៃ​ចុងក្រោយ"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"ខែ​មុន"</string>
     <string name="older" msgid="5211975022815554840">"ចាស់​ជាង"</string>
   <plurals name="num_days_ago">
@@ -1305,6 +1303,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android កំពុង​ចាប់ផ្ដើម…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"កំពុងធ្វើឲ្យឧបករណ៍ផ្ទុកមានប្រសិទ្ធភាព។"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"ធ្វើ​ឲ្យ​កម្មវិធី​ប្រសើរ​ឡើង <xliff:g id="NUMBER_0">%1$d</xliff:g> នៃ <xliff:g id="NUMBER_1">%2$d</xliff:g> ។"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"កំពុងរៀបចំ <xliff:g id="APPNAME">%1$s</xliff:g>។"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"ចាប់ផ្ដើម​កម្មវិធី។"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"បញ្ចប់​ការ​ចាប់ផ្ដើម។"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
@@ -1350,7 +1349,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"មិន​​អាច​តភ្ជាប់​វ៉ាយហ្វាយ"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" មាន​ការ​តភ្ជាប់​អ៊ីនធឺណិត​មិន​ល្អ។"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"អនុញ្ញាត​ភ្ជាប់?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ចង់​ភ្ជាប់​ %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"កម្មវិធី %1$s ចង់ភ្ជាប់ទៅបណ្តាញ Wifi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"កម្មវិធី"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"វ៉ាយហ្វាយ​ផ្ទាល់"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"ចាប់ផ្ដើម​វ៉ាយហ្វាយ​ផ្ទាល់។ វា​នឹង​បិទ​វ៉ាយហ្វាយ​ហតស្ពត។"</string>
@@ -1881,4 +1880,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"សំណើរ SS ត្រូវបានកែសម្រួលទៅតាមសំណើរការហៅទូរស័ព្ទ។"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"សំណើរ SS ត្រូវបានកែសម្រួលទៅតាមសំណើរ USSD។"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"សំណើរ SS ត្រូវបានកែសម្រួលទៅតាមសំណើរ SS ថ្មី។"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"ឧបករណ៍រន្ធ USB បន្ថែម"</string>
 </resources>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 0d18fce..befcfd2 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 ಗಂಟೆ ಹಿಂದೆ"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ಗಂಟೆಗಳ ಹಿಂದೆ"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"ಕಳೆದ <xliff:g id="COUNT">%d</xliff:g> ದಿನಗಳಲ್ಲಿ"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"ಕಳೆದ ತಿಂಗಳು"</string>
     <string name="older" msgid="5211975022815554840">"ಹಳೆಯದು"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ಸಂಗ್ರಹಣೆಯನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="NUMBER_0">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್‌ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"ಬೂಟ್ ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> ರನ್ ಆಗುತ್ತಿದೆ"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi ಗೆ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ಕಳಪೆ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿದೆ."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"ಸಂಪರ್ಕವನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ಅವರು %2$s ಗೆ ಸಂಪರ್ಕಿಸಲು ಬಯಸುತ್ತಾರೆ"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%2$s ವೈಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಸಂಪರ್ಕಿಸಲು %1$s ಅಪ್ಲಿಕೇಶನ್‌ ಬಯಸುತ್ತದೆ"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"ಅಪ್ಲಿಕೇಶನ್"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ಡೈರೆಕ್ಟ್"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi ಡೈರೆಕ್ಟ್ ಪ್ರಾರಂಭಿಸಿ. ಇದು Wi-Fi ಕ್ಲೈಂಟ್‌/ಹಾಟ್‌ಸ್ಪಾಟ್ ಅನ್ನು ಆಫ್ ಮಾಡುತ್ತದೆ."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ವಿನಂತಿಯನ್ನು DIAL ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ವಿನಂತಿಯನ್ನು USSD ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ವಿನಂತಿಯನ್ನು ಹೊಸ SS ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB ಹೊರಭಾಗದ ಪೋರ್ಟ್"</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c52c351..7594e18 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1시간 전"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g>시간 전"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"지난 <xliff:g id="COUNT">%d</xliff:g>일"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"지난 달"</string>
     <string name="older" msgid="5211975022815554840">"이전"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android가 시작되는 중…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"저장소 최적화 중"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"앱 <xliff:g id="NUMBER_1">%2$d</xliff:g>개 중 <xliff:g id="NUMBER_0">%1$d</xliff:g>개 최적화 중"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"앱을 시작하는 중입니다."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"부팅 완료"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 실행 중"</string>
@@ -1348,8 +1348,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi에 연결할 수 없습니다"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 인터넷 연결 상태가 좋지 않습니다."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"연결을 허용하시겠습니까?"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for wifi_connect_alert_message (8930084523889618078) -->
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
     <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"앱"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
@@ -1881,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 요청이 DIAL 요청으로 수정됩니다."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 요청이 USSD 요청으로 수정됩니다."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 요청이 새로운 SS 요청으로 수정됩니다."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 2b45690..9675fdc 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1479,6 +1479,7 @@
     <!-- no translation found for num_minutes_ago:other (2176942008915455116) -->
     <!-- no translation found for num_hours_ago:one (9150797944610821849) -->
     <!-- no translation found for num_hours_ago:other (2467273239587587569) -->
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <!-- no translation found for last_num_days:other (3069992808164318268) -->
     <!-- no translation found for last_month (3959346739979055432) -->
     <skip />
@@ -1661,6 +1662,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android жүргүзүлүүдө…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Сактагыч ыңгайлаштырылууда."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо ыңгайлаштырылууда."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Колдонмолорду иштетип баштоо"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Жүктөө аякталууда."</string>
     <!-- no translation found for heavy_weight_notification (9087063985776626166) -->
@@ -1717,7 +1719,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi менен туташуу түзүлбөдү"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" хотспотунун интернет байланышы начар."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Туташууга уруксатпы?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s %2$s менен туташкысы келди"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s колдонмосу %2$s Wifi тармагына туташкысы келет"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Колдонмо"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Дайректи иштетүү. Бул Wi-Fi клиентти/хотспотту өчүрөт."</string>
@@ -2357,4 +2359,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS сурамы DIAL сурамына өзгөртүлдү."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS сурамы USSD сурамына өзгөртүлдү."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS сурамы жаңы SS сурамына өзгөртүлдү."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB Сырткы оюкча"</string>
 </resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index ed47bbb..21644ec 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 ຊົ່ວໂມງກ່ອນໜ້ານີ້"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ຊົ່ວໂມງທີ່ຜ່ານມາ"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> ມື້ທີ່ຜ່ານມາ"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"ເດືອນແລ້ວ"</string>
     <string name="older" msgid="5211975022815554840">"ເກົ່າກວ່າ"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"ກຳ​ລັງ​ເລີ່ມລະ​ບົບ​ Android …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ການ​ປັບ​ບ່ອນ​ເກັບ​ຂໍ້​ມູນ​ໃຫ້​ເໝາະ​ສົມ."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"ກຳລັງ​ປັບປຸງ​ປະສິດທິພາບ​ແອັບຯ​ທີ <xliff:g id="NUMBER_0">%1$d</xliff:g> ຈາກ​ທັງ​ໝົດ <xliff:g id="NUMBER_1">%2$d</xliff:g> ແອັບຯ."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"ກຳ​ລັງ​ກຽມ <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"ກຳລັງເປີດແອັບຯ."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"ກຳລັງສຳເລັດການເປີດລະບົບ."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງເຮັດວຽກ"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"ບໍ່ສາມາດເຊື່ອມຕໍ່ Wi-Fi ໄດ້"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ມີສັນຍານອິນເຕີເນັດທີ່ບໍ່ດີ."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"​ອະ​ນຸ​ຍາດ​ການ​ເຊື່ອມ​ຕໍ່ຫຼື​ບໍ່?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ​ຕ້ອງ​ການ​ທີ່​ຈະ​ເຊື່ອມ​ຕໍ່​ຫາ %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"ແອັບ​ພ​ລິ​ເຄ​ຊັນ %1$s ຢາກ​ຈະ​ເຊື່ອມ​ຕໍ່​ກັບ​ເຄືອ​ຂ່າຍ Wifi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"​ແອັບ​ພລິ​ເຄ​ຊັນ"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"ເລີ່ມ Wi-Fi Direct. ນີ້ຈະເປັນການປິດ Wi-Fi client/hotspot."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"ການ​ຂໍ SS ຖືກ​ດັດ​ແປງ​ເປັນ​ການ​ຂໍ DIAL ແລ້ວ."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"ການ​ຂໍ SS ຖືກ​ດັດ​ແປງ​ເປັນ​ການ​ຂໍ USSD ແລ້ວ."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"ການ​ຂໍ SS ຖືກ​ດັດ​ແປງ​ເປັນ​ການ​ຂໍ SS ໃໝ່​ແລ້ວ."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"ຊ່ອງ​ຮອບນອກ USB"</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index f22ddea..9e5bcb5 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Prieš 1 valandą"</item>
     <item quantity="other" msgid="2467273239587587569">"Prieš <xliff:g id="COUNT">%d</xliff:g> val."</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Paskutinės <xliff:g id="COUNT">%d</xliff:g> dienos (-ų)"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Paskutinį mėnesį"</string>
     <string name="older" msgid="5211975022815554840">"Senesni"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Paleidžiama „Android“…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimizuojama saugykla."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimizuojama <xliff:g id="NUMBER_0">%1$d</xliff:g> progr. iš <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Paleidžiamos programos."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Užbaigiamas paleidimas."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Vykdoma „<xliff:g id="APP">%1$s</xliff:g>“"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nepavyko prisijungti prie „Wi-Fi“"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" turi prastą interneto ryšį."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Leisti prisijungti?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"„%1$s“ nori prisijungti prie „%2$s“"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Programa"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Tiesioginis „Wi-Fi“ ryšys"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Paleiskite „Wi-Fi Direct“. Bus išjungta „Wi-Fi“ programa / viešosios interneto prieigos taškas."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS užklausa pakeista į DIAL užklausą."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS užklausa pakeista į USSD užklausą."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS užklausa pakeista į naują SS užklausą."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 1c1f0c5..20bad77 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Pirms 1 stundas"</item>
     <item quantity="other" msgid="2467273239587587569">"Pirms <xliff:g id="COUNT">%d</xliff:g> stundas(-ām)"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Pēdējās <xliff:g id="COUNT">%d</xliff:g> dienās"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Iepriekšējā mēnesī"</string>
     <string name="older" msgid="5211975022815554840">"Vecāks"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Notiek Android palaišana…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Notiek krātuves optimizēšana."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Tiek optimizēta <xliff:g id="NUMBER_0">%1$d</xliff:g>. lietotne no <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Notiek lietotņu palaišana."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Tiek pabeigta sāknēšana."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> darbojas"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nevarēja izveidot savienojumu ar Wi-Fi."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ir slikts interneta savienojums."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vai atļaut savienojumu?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s vēlas izveidot savienojumu ar tīklu %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Lietojumprogramma"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Palaist programmu Wi-Fi Direct. Tādējādi tiks izslēgta Wi-Fi klienta/tīklāja darbība."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS pieprasījums ir mainīts uz DIAL pieprasījumu."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS pieprasījums ir mainīts uz USSD pieprasījumu."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS pieprasījums ir mainīts uz jaunu SS pieprasījumu."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index df4b13b..72dd8e2 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Пред 1 час"</item>
     <item quantity="other" msgid="2467273239587587569">"пред <xliff:g id="COUNT">%d</xliff:g> часа"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Последните <xliff:g id="COUNT">%d</xliff:g> дена"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Минатиот месец"</string>
     <string name="older" msgid="5211975022815554840">"Постари"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android стартува…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизирање на складирањето."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Се оптимизира апликација <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Се подготвува <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Се стартуваат апликациите."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Подигањето завршува."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> работи"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не можеше да се поврзе со Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" има слаба конекција на интернет."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Дозволете поврзување?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s сака да се поврзе на %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Апликацијата %1$s сака да се поврзе со Wifi-мрежата %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Апликација"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Започни Wi-Fi Direct. Ова ќе го исклучи Wi-Fi клиентот/хточката на пристап."</string>
@@ -1881,4 +1880,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Барањето SS е изменето во барање DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Барањето SS е изменето во барање USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Барањето SS е изменето во ново барање SS."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Надворешна порта на УСБ"</string>
 </resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index b9bfa99..134a9b5 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 മണിക്കൂര്‍ മുമ്പ്"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> മണിക്കൂർ മുമ്പ്"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"കഴിഞ്ഞ <xliff:g id="COUNT">%d</xliff:g> ദിവസം"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"കഴിഞ്ഞ മാസം"</string>
     <string name="older" msgid="5211975022815554840">"പഴയത്"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android ആരംഭിക്കുന്നു…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"സംഭരണം ഒപ്‌റ്റിമൈസ് ചെയ്യുന്നു."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> അപ്ലിക്കേഷൻ ഓപ്റ്റിമൈസ് ചെയ്യുന്നു."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> തയ്യാറാക്കുന്നു."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"ബൂട്ട് ചെയ്യൽ പൂർത്തിയാകുന്നു."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> പ്രവർത്തിക്കുന്നു"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi-ലേക്ക് കണക്‌റ്റുചെയ്യാൻ കഴിഞ്ഞില്ല"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" മോശം ഇന്റർനെറ്റ് കണക്ഷനാണുള്ളത്."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"കണക്ഷൻ അനുവദിക്കണോ?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s, %2$s എന്നതിലേക്ക് കണക്‌റ്റുചെയ്യാൻ ആഗ്രഹിക്കുന്നു"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"അപ്ലിക്കേഷൻ %1$s Wifi നെറ്റ്‌വർക്കിലേക്ക് കണക്‌റ്റുചെയ്യാൻ താൽപ്പര്യപ്പെടുന്നു %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"ഒരു അപ്ലിക്കേഷൻ"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ഡയറക്‌ട്"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi ഡയറക്റ്റ് ആരംഭിക്കുക. ഇത് Wi-Fi ക്ലയന്റ്/ഹോട്ട്‌സ്‌പോട്ട് ഓഫാക്കും."</string>
@@ -1877,4 +1876,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS അഭ്യർത്ഥന, DIAL അഭ്യർത്ഥനയായി പരിഷ്‌ക്കരിച്ചു."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS അഭ്യർത്ഥന, USSD അഭ്യർത്ഥനയായി പരിഷ്‌ക്കരിച്ചു."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS അഭ്യർത്ഥന, പുതിയ SS അഭ്യർത്ഥനയായി പരിഷ്‌ക്കരിച്ചു."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB പെരിഫറൽ പോർട്ട്"</string>
 </resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index a14588d..da08b72 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 цагийн өмнө"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> цагийн өмнө"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Сүүлийн <xliff:g id="COUNT">%d</xliff:g> өдөр"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Сүүлийн сар"</string>
     <string name="older" msgid="5211975022815554840">"Хуучин"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Андройд эхэлж байна..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Хадгалалтыг сайжруулж байна."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>-н <xliff:g id="NUMBER_0">%1$d</xliff:g> апп-г тохируулж байна."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Бэлдэж байна <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Апп-г эхлүүлж байна."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Эхлэлийг дуусгаж байна."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> ажиллаж байна"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi-д холбогдож чадсангүй"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" Интернет холболт муу байна."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Холболтыг зөвшөөрөх үү?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s %2$s-тай холбогдохыг хүсэж байна"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Програм %1$s нь Wifi сүлжээ %2$s-тай холбох хүсэлтэй байна"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Аппликешн"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Шууд"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Шуудыг эхлүүлнэ үү. Энэ нь Wi-Fi клиент/холболтын цэг унтраана."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS хүсэлтийг DIAL хүсэлт болгон өөрчилсөн байна"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS хүсэлтийг USSD хүсэлт болгон өөрчилсөн байна."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS хүсэлтийг шинэ SS хүсэлт болгон өөрчилсөн байна."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB Peripheral Port"</string>
 </resources>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 55daa3a..a2c21bd 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -218,7 +218,7 @@
     <string name="permgroupdesc_location" msgid="5704679763124170100">"आपल्या प्रत्यक्ष स्थानाचे परीक्षण करेल."</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"नेटवर्क संप्रेषण"</string>
     <string name="permgroupdesc_network" msgid="4478299413241861987">"विविध नेटवर्क वैशिष्ट्यांवर प्रवेश करेल."</string>
-    <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"ब"</string>
+    <string name="permgrouplab_bluetoothNetwork" msgid="1585403544162128109">"ब्लूटुथ"</string>
     <string name="permgroupdesc_bluetoothNetwork" msgid="5625288577164282391">"ब द्वारे डिव्हाइसेसवर आणि नेटवर्कवर प्रवेश करेल."</string>
     <string name="permgrouplab_audioSettings" msgid="8329261670151871235">"ऑडिओ सेटिंग्ज"</string>
     <string name="permgroupdesc_audioSettings" msgid="2641515403347568130">"ऑडिओ सेटिंग्ज बदला."</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 तासापूर्वी"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> तासांपूर्वी"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"अंतिम <xliff:g id="COUNT">%d</xliff:g> दिवस"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"अंतिम महिना"</string>
     <string name="older" msgid="5211975022815554840">"अधिक जुने"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android प्रारंभ करत आहे…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"संचयन ऑप्टिमाइझ करत आहे."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप ऑप्टिमाइझ करत आहे."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करीत आहे."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"अॅप्स प्रारंभ करत आहे."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"बूट समाप्त होत आहे."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> चालत आहे"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"वाय-फाय ला कनेक्ट करू शकलो नाही"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" खराब इंटरनेट कनेक्शन आहे."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"कनेक्शनला अनुमती द्यायची?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s %2$s शी कनेक्ट करू इच्छितात"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s अनुप्रयोग %2$s वायफाय नेटवर्कशी कनेक्ट करू इच्छित आहे"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"अनुप्रयोग"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"वाय-फाय थेट"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"वाय-फाय थेट प्रारंभ करा. हे वाय-फाय क्लायंट/हॉटस्पॉट बंद करेल."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS विनंती डायल विनंतीवर सुधारित केली आहे."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS विनंती USSD विनंतीवर सुधारित केली आहे."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS विनंती नवीन SS विनंतीवर सुधारित केली आहे."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB बाह्यवर्ती पोर्ट"</string>
 </resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 7ec40c4..3ca56d7 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 jam yang lalu"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> jam yang lalu"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> hari terakhir"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Bulan lepas"</string>
     <string name="older" msgid="5211975022815554840">"Lebih lama"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android sedang dimulakan…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Mengoptimumkan storan."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Mengoptimumkan apl <xliff:g id="NUMBER_0">%1$d</xliff:g> daripada <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Menyediakan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Memulakan apl."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"But akhir."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> dijalankan"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Tidak boleh menyambung kepada Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" mempunyai sambungan internet yang kurang baik."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Benarkan sambungan?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ingin menyambung ke %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Aplikasi %1$s ingin menyambung ke Rangkaian Wifi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Satu aplikasi"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Langsung"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Mulakan Wi-Fi Langsung. Hal ini akan mematikan pengendalian klien/liputan Wi-Fi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Permintaan SS diubah kepada permintaan DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Permintaan SS diubah kepada permintaan USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Permintaan SS diubah kepada permintaan SS baharu."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Port Persisian USB"</string>
 </resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 6e0b5ad..c8fb23c 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"လွနခဲ့သော ၁နာရီက"</item>
     <item quantity="other" msgid="2467273239587587569">"လွန်ခဲ့သော <xliff:g id="COUNT">%d</xliff:g> နာရီက"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"ပြီးခဲ့သော<xliff:g id="COUNT">%d</xliff:g>ရက်က"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"ပြီးခဲ့သောလ"</string>
     <string name="older" msgid="5211975022815554840">"ယခင်က"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android စတင်နေ…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"သိုလှောင်မှုအား ပြုပြင်ခြင်း။"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> ထဲက app<xliff:g id="NUMBER_1">%2$d</xliff:g>ကို ဆီလျော်အောင် လုပ်နေ"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> အားပြင်ဆင်နေသည်။"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"appများကို စတင်နေ"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"လုပ်ငန်းစနစ်ထည့်သွင်း၍ ပြန်လည်စတင်ရန် ပြီးပါပြီ"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> က အလုပ်လုပ်နေသည်"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"ဝိုင်ဖိုင်ကိုချိတ်ဆက်မရပါ"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" အင်တာနက် ဆက်သွယ်မှု ကောင်းကောင်းမရှိပါ"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"ချိတ်ဆက်မှုကို ခွင့်ပြုမလား?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s က %2$s သို့ ချိတ်ဆက်ချင်"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"အပ္ပလီကေးရှင်း %1$s သည် ဝိုင်ဖိုင်ကွန်ရက် %2$s ကိုချိတ်ဆက်လိုသည်"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"အပလီကေးရှင်း"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"တိုက်ရိုက် ဝိုင်ဖိုင်"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"တိုက်ရိုက်ဝိုင်ဖိုင်ကို စတင်ပါ။ ၎င်းသည် ဝိုင်ဖိုင် ဟော့စပေါ့ကို ရပ်ဆိုင်းစေမှာ ဖြစ်ပါသည်။"</string>
@@ -1877,4 +1876,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"DIAL တောင်းဆိုချက်အရ SS တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"USSD တောင်းဆိုချက်အရ SS တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS တောင်းဆိုချက်အရ SS တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"အန်းဒရွိုက်"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB ဘေးရှိပို့တ်"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index dc38563..856e2d3 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"for en time siden"</item>
     <item quantity="other" msgid="2467273239587587569">"for <xliff:g id="COUNT">%d</xliff:g> timer siden"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Siste <xliff:g id="COUNT">%d</xliff:g> dager"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Forrige måned"</string>
     <string name="older" msgid="5211975022815554840">"Eldre"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android starter …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimaliser lagring."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimaliserer app <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Starter apper."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Ferdigstiller oppstart."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> kjører"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan ikke koble til Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" har en dårlig Internett-tilkobling."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vil du tillat tilkoblingen?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s prøver å koble til %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Appen %1$s vil koble til Wi-Fi-nettverket %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"En app"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. Dette deaktiverer Wi-Fi-klienten/-sonen."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-forespørselen er endret til en RINGE-forespørsel."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-forespørselen er endret til en USSD-forespørsel."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-forespørselen er endret til en ny SS-forespørsel."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Port for USB-tilleggsutstyr"</string>
 </resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index fb8e1c2..73aa852 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"१ घन्टा अघि"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> घन्टा अघि"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"अन्तिम <xliff:g id="COUNT">%d</xliff:g> दिन"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"अन्तिम महिना"</string>
     <string name="older" msgid="5211975022815554840">"पुरानो"</string>
   <plurals name="num_days_ago">
@@ -1309,6 +1307,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android शुरू हुँदैछ..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"भण्डारण अनुकूलन गर्दै।"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"अनुप्रयोग अनुकुल हुँदै <xliff:g id="NUMBER_0">%1$d</xliff:g> को <xliff:g id="NUMBER_1">%2$d</xliff:g>।"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> तयारी गर्दै।"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"सुरुवात अनुप्रयोगहरू।"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"बुट पुरा हुँदै।"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> चलिरहेको छ"</string>
@@ -1354,7 +1353,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"वाइ-फाइसँग जडान गर्न सकेन"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" कमजोर इन्टरनेट जडान छ।"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"जडान अनुमति दिने हो?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ले %2$s मा जडान गर्न चाहन्छ"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"अनुप्रयोग %1$s वाइफाइ नेटवर्क %2$s मा जडान गर्न चाहन्छ"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"एउटा अनुप्रयोग"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"वाइफाइ प्रत्यक्ष"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"वाइफाइ सिधा सुरु गर्नुहोस्। यसले वाइफाइ ग्राहक/हट्स्पटलाई बन्द गराउने छ।"</string>
@@ -1885,4 +1884,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS अनुरोध  DIAL अनुरोधमा परिमार्जन गरिएको छ।"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS अनुरोध USSD अनुरोधमा परिमार्जन गरिएको छ।"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS अनुरोध नयाँ SS अनुरोधमा परिमार्जन गरिएको छ।"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB परिधीय पोर्ट"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 75f4a74..fc496aa 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 uur geleden"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> uur geleden"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Afgelopen <xliff:g id="COUNT">%d</xliff:g> dagen"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Afgelopen maand"</string>
     <string name="older" msgid="5211975022815554840">"Ouder"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android wordt gestart…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Opslagruimte wordt geoptimaliseerd."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g> optimaliseren."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> voorbereiden."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Apps starten."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Opstarten afronden."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> wordt uitgevoerd"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan geen verbinding maken met wifi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" heeft een slechte internetverbinding."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Verbinding toestaan?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s wilt verbinding maken met %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"App %1$s wil verbinding maken met wifi-netwerk %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Een app"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wifi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wifi Direct starten. Hierdoor wordt de wifi-client/hotspot uitgeschakeld."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-verzoek is gewijzigd in DIAL-verzoek."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-verzoek is gewijzigd in USSD-verzoek."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-verzoek is gewijzigd in nieuw SS-verzoek."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Poort voor USB-randapparatuur"</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 2482b0a..06cca3b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -269,13 +269,13 @@
     <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Dostęp do karty SD."</string>
     <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Funkcje ułatwień dostępu"</string>
     <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Funkcje, których może zażądać technologia ułatwień dostępu."</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Pobierz zawartość okna"</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Pobieranie zawartości okna"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Sprawdzanie zawartości okna, z którego korzystasz."</string>
-    <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Włącz czytanie dotykiem"</string>
+    <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Włączenie czytania dotykiem"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"Klikane elementy będą wymawiane na głos, a ekran można przeglądać, używając gestów."</string>
-    <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Włącz ułatwienia dostępu w internecie"</string>
+    <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Włączenie ułatwień dostępu w internecie"</string>
     <string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Można zainstalować skrypty, by zawartość aplikacji była łatwiej dostępna."</string>
-    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Obserwuj wpisywany tekst"</string>
+    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Obserwowanie wpisywanego tekstu"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obejmuje informacje osobiste, takie jak numery kart kredytowych i hasła."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"wyłączanie lub zmienianie paska stanu"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"godzinę temu"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> godz. temu"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Ostatnie (<xliff:g id="COUNT">%d</xliff:g>) dni"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Ostatni miesiąc"</string>
     <string name="older" msgid="5211975022815554840">"Starsze"</string>
   <plurals name="num_days_ago">
@@ -1200,7 +1198,7 @@
     <item quantity="other" msgid="2973062968038355991">"za <xliff:g id="COUNT">%d</xliff:g> dni"</item>
   </plurals>
     <string name="preposition_for_date" msgid="9093949757757445117">"w dniu <xliff:g id="DATE">%s</xliff:g>"</string>
-    <string name="preposition_for_time" msgid="5506831244263083793">"o godzinie <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="preposition_for_time" msgid="5506831244263083793">"o godzinie <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="preposition_for_year" msgid="5040395640711867177">"w <xliff:g id="YEAR">%s</xliff:g> r."</string>
     <string name="day" msgid="8144195776058119424">"dzień"</string>
     <string name="days" msgid="4774547661021344602">"dni"</string>
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android się uruchamia…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optymalizacja pamięci."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optymalizowanie aplikacji <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Uruchamianie aplikacji."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Kończenie uruchamiania."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Działa <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nie można połączyć się z siecią Wi-Fi."</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ma powolne połączenie internetowe."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Zezwolić na połączenie?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s chce połączyć się z: %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikacja"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Uruchom Wi-Fi Direct. Spowoduje to wyłączenie klienta lub punktu dostępu Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Żądanie SS zostało zmienione na żądanie DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Żądanie SS zostało zmienione na żądanie USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Żądanie SS zostało zmienione na nowe żądanie SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 40e9313..b5e5484 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Há 1 hora"</item>
     <item quantity="other" msgid="2467273239587587569">"Há <xliff:g id="COUNT">%d</xliff:g> horas"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> dias"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Último mês"</string>
     <string name="older" msgid="5211975022815554840">"Mais antiga"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"O Android está a iniciar…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"A otimizar o armazenamento."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"A otimizar a aplicação <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"A preparar o <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"A iniciar aplicações"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"A concluir o arranque."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Não foi possível ligar a Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tem uma ligação à internet fraca."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Permitir ligação?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s pretende ligar a %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"A aplicação %1$s pretende estabelecer ligação à rede Wi-Fi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Uma aplicação"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar o Wi-Fi Direct. Esta opção desativará o cliente/zona Wi-Fi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"O pedido SS foi modificado para um pedido DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"O pedido SS foi modificado para um pedido USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"O pedido SS foi modificado para um novo pedido SS."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Porta periférica USB"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a11ba9d..5aab6e2 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 hora atrás"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> horas atrás"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Últimos <xliff:g id="COUNT">%d</xliff:g> dias"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Mês passado"</string>
     <string name="older" msgid="5211975022815554840">"Mais antigos"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"O Android está iniciando..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Otimizando o armazenamento."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Otimizando app <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Iniciando apps."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Concluindo a inicialização."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Não foi possível se conectar a redes Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tem uma conexão de baixa qualidade com a Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Permitir conexão?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s gostaria de se conectar a %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Um app"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar o Wi-Fi Direct. Isso desativará o ponto de acesso/cliente Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"A solicitação SS foi modificada para a solicitação DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"A solicitação SS foi modificada para a solicitação USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"A solicitação SS foi modificada para a nova solicitação SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 625f720..4bd2497 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"cu 1 oră în urmă"</item>
     <item quantity="other" msgid="2467273239587587569">"cu <xliff:g id="COUNT">%d</xliff:g> (de) ore în urmă"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Ultimele <xliff:g id="COUNT">%d</xliff:g> de zile"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Luna trecută"</string>
     <string name="older" msgid="5211975022815554840">"Mai vechi"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android pornește..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Se optimizează stocarea."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Se optimizează aplicaţia <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Se pornesc aplicaţiile."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Se finalizează pornirea."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nu se poate conecta la Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" are o conexiune la internet slabă."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Permiteți conectarea?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s dorește să se conecteze la %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"O aplicație"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Porniţi Wi-Fi Direct. Acest lucru va dezactiva clientul/hotspotul Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Solicitarea SS este modificată într-o solicitare DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Solicitarea SS este modificată într-o solicitare USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Solicitarea SS este modificată într-o nouă solicitare SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index a768aa9..48e4a23 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 час назад"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ч. назад"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Последние <xliff:g id="COUNT">%d</xliff:g> дн."</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Прошлый месяц"</string>
     <string name="older" msgid="5211975022815554840">"Еще раньше"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Запуск Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимизация хранилища…"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимизация приложения <xliff:g id="NUMBER_0">%1$d</xliff:g> из <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Запуск приложений."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Окончание загрузки..."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не удалось подключиться к сети Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" – плохое интернет-соединение."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Разрешить подключение?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s запрашивает доступ на подключение к %2$s."</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Приложение"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Начать соединение через Wi-Fi Direct. Модуль Wi-Fi будет отключен."</string>
@@ -1846,7 +1847,7 @@
     <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Запрашивать PIN-код для отключения блокировки"</string>
     <string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Запрашивать графический ключ для отключения блокировки"</string>
     <string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Запрашивать пароль для отключения блокировки"</string>
-    <string name="battery_saver_description" msgid="1960431123816253034">"Чтобы заряд батареи расходовался медленнее, режим энергосбережения уменьшает быстродействие устройства и ограничивает количество ресурсов, затрачиваемых на вибрацию, Геолокацию и большинство процессов обработки данных в фоновом режиме. Приложения, которые используют синхронизацию (например, для электронной почты и обмена SMS), могут не обновляться, пока вы их не откроете.\n\nКогда устройство заряжается, режим энергосбережения отключается автоматически."</string>
+    <string name="battery_saver_description" msgid="1960431123816253034">"Чтобы продлить время работы устройства от батареи, в режиме энергосбережения снижается производительность, а также ограничивается использование вибрации, геолокации и фоновой передачи данных. Данные, требующие синхронизации, могут обновляться только когда вы откроете приложение.\n\nРежим энергосбережения автоматически отключается во время зарядки устройства."</string>
     <string name="downtime_condition_summary" msgid="8761776337475705749">"До отключения режима (в <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>)"</string>
     <string name="downtime_condition_line_one" msgid="8762708714645352010">"До отключения режима"</string>
   <plurals name="zen_mode_duration_minutes_summary">
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-запрос преобразован в DIAL-запрос."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-запрос преобразован в USSD-запрос."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-запрос преобразован в новый SS-запрос."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 3cde8f6..559e865 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -1144,9 +1144,7 @@
     <item quantity="one" msgid="9150797944610821849">"පැය 1 කට පෙර"</item>
     <item quantity="other" msgid="2467273239587587569">"පැය <xliff:g id="COUNT">%d</xliff:g> කට පෙර"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"අන්තිම දවස් <xliff:g id="COUNT">%d</xliff:g>"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"අවසාන මාසය"</string>
     <string name="older" msgid="5211975022815554840">"පරණ"</string>
   <plurals name="num_days_ago">
@@ -1305,6 +1303,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android ආරම්භ කරමින්…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"ආචයනය ප්‍රශස්තිකරණය කිරීම."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g> කින් <xliff:g id="NUMBER_0">%1$d</xliff:g> වැනි යෙදුම ප්‍රශස්ත කරමින්."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> සූදානම් කරමින්."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"යෙදුම් ආරම්භ කරමින්."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"ඇරඹුම අවසාන කරමින්."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> ධාවනය වෙමින්"</string>
@@ -1350,7 +1349,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi වෙත සම්බන්ධ විය නොහැක"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" දුබල අන්තර්ජාල සම්බන්ධතාවයක් ඇත."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"සම්බන්ධතාවයට ඉඩ දෙන්නද?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%2$s වෙත සම්බන්ධවීමට %1$s කැමතිය"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"යෙදුම් %1$s ක් WiFi ජාලය %2$s වෙත සබැඳීමට කැමතියි"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"යෙදුම"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"ඍජු Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"ඍජු Wi-Fi ආරම්භ කරන්න. මෙය Wi-Fi සේවාදායක/හොට්ස්පොට් එක අක්‍රිය කරනු ඇත."</string>
@@ -1881,4 +1880,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ඉල්ලීම DIAL ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ඉල්ලීම USSD ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ඉල්ලීම නව DIAL ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"ඇන්ඩ්රොයිඩ්"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB පර්යන්ත තොට"</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 4b56f04..f275f85 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"pred 1 hodinou"</item>
     <item quantity="other" msgid="2467273239587587569">"pred <xliff:g id="COUNT">%d</xliff:g> hodinami"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Posledných <xliff:g id="COUNT">%d</xliff:g> dní"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Minulý mesiac"</string>
     <string name="older" msgid="5211975022815554840">"Staršie"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Systém Android sa spúšťa…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimalizuje sa úložisko"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Prebieha optimalizácia aplikácie <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Prebieha spúšťanie aplikácií."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Prebieha dokončovanie spúšťania."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Spustená aplikácia: <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nepodarilo sa pripojiť k sieti Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" má nekvalitné internetové pripojenie."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Povoliť pripojenie?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"Zariadenie %1$s sa chce pripojiť k sieti %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikácia"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Priame pripojenie Wi-Fi"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Spustiť priame pripojenie siete Wi-Fi. Táto možnosť vypne sieť Wi-Fi v režime klient alebo hotspot."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Žiadosť SS bola upravená na žiadosť DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Žiadosť SS bola upravená na žiadosť USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Žiadosť SS bola upravená na novú žiadosť SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index dca7d4e..a53aac8 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"pred 1 uro"</item>
     <item quantity="other" msgid="2467273239587587569">"Pred <xliff:g id="COUNT">%d</xliff:g> urami"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Zadnjih <xliff:g id="COUNT">%d</xliff:g> dni"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Pretekli mesec"</string>
     <string name="older" msgid="5211975022815554840">"Starejše"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android se zaganja …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Optimiziranje shrambe."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimiranje aplikacije <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Zagon aplikacij."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Dokončevanje zagona."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> se izvaja"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Z omrežjem Wi-Fi se ni mogoče povezati"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ima slabo internetno povezavo."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Ali dovolite vzpostavitev povezave?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s želi vzpostaviti povezavo s tem: %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Aplikacija"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Zaženite Wi-Fi Direct. S tem boste izklopili odjemalca/dostopno točko Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Zahteva SS je spremenjena v zahtevo DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Zahteva SS je spremenjena v zahtevo USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Zahteva SS je spremenjena v novo zahtevo SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index de9029f..b1510b6 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Пре сат времена"</item>
     <item quantity="other" msgid="2467273239587587569">"пре <xliff:g id="COUNT">%d</xliff:g> сата(и)"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"У последња(их) <xliff:g id="COUNT">%d</xliff:g> дана"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Прошлог месеца"</string>
     <string name="older" msgid="5211975022815554840">"Старије"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android се покреће…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Меморија се оптимизује."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимизовање апликације <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Покретање апликација."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Завршавање покретања."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Није могуће повезати са Wi-Fi мрежом"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" има лошу интернет везу."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Желите ли да дозволите повезивање?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s жели да се повеже са мрежом %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Апликација"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Покрените Wi-Fi Direct. Тиме ћете искључити клијента/хотспот за Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS захтев је промењен у DIAL захтев."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS захтев је промењен у USSD захтев."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS захтев је промењен у нови SS захтев."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index ad5c495..8fbf645 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"för 1 timme sedan"</item>
     <item quantity="other" msgid="2467273239587587569">"för <xliff:g id="COUNT">%d</xliff:g> timmar sedan"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"De senaste <xliff:g id="COUNT">%d</xliff:g> dagarna"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Föregående månad"</string>
     <string name="older" msgid="5211975022815554840">"Äldre"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android startar …"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Lagringsutrymmet optimeras."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Optimerar app <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> förbereds."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Appar startas."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Uppgraderingen är klar."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> körs"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Det gick inte att ansluta till Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" har en dålig Internetanslutning."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Tillåt anslutning?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s vill ansluta till %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Appen %1$s vill ansluta till Wi-Fi-nätverket %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"En app"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi direkt"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Starta direkt Wi-Fi-användning. Detta inaktiverar Wi-Fi-användning med klient/trådlös surfzon."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-begäran har ändrats till en DIAL-begäran."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-begäran har ändrats till en USSD-begäran."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-begäran har ändrats till en ny SS-begäran."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Perifer USB-port"</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 2db8cd6..b2b21e3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"Saa 1 iliyopita"</item>
     <item quantity="other" msgid="2467273239587587569">"Saa <xliff:g id="COUNT">%d</xliff:g> zilizopita"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Siku <xliff:g id="COUNT">%d</xliff:g> zilizopita"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Mwezi uliopita"</string>
     <string name="older" msgid="5211975022815554840">"Kuukuu zaidi"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Inaanzisha Android..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Inaboresha hifadhi."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Inaboresha programu <xliff:g id="NUMBER_0">%1$d</xliff:g> kutoka <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Programu zinaanza"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Inamaliza kuwasha."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Haikuweza kuunganisha kwa Mtandao-Hewa"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ina muunganisho duni wa Mtandao."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Ungepenga kuruhusu muunganisho?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s angependa kuunganisha kwenye %2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Programu"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Mtandao hewa Moja kwa moja"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Anzisha Wi-Fi Moja kwa Moja. Hii itazima mteja/mtandao-hewa wa Wi-Fi."</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Ombi la SS limerekebishwa na kuwa ombi la DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Ombi la SS limerekebishwa na kuwa ombi la USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Ombi la SS limerekebishwa na kuwa ombi jipya la SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 570671b..c8deaa7 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 மணிநேரம் முன்பு"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> மணிநேரத்திற்கு முன்பு"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"கடந்த <xliff:g id="COUNT">%d</xliff:g> நாட்கள்"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"சென்ற மாதம்"</string>
     <string name="older" msgid="5211975022815554840">"பழையது"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android துவங்குகிறது..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"சேமிப்பகத்தை உகந்ததாக்குகிறது."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> பயன்பாட்டை ஒருங்கிணைக்கிறது."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"பயன்பாடுகள் தொடங்கப்படுகின்றன."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"துவக்குதலை முடிக்கிறது."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"வைஃபை உடன் இணைக்க முடியவில்லை"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" இணைய இணைப்பு மோசமாக உள்ளது."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"இணைப்பை அனுமதிக்கவா?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s , %2$s உடன் இணைக்க விரும்புகிறது"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%2$s வைஃபை நெட்வொர்க்குடன், %1$s பயன்பாடு இணைக்க விரும்புகிறது"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"ஒரு பயன்பாடு"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"வைஃபை Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"வைஃபை Direct ஐத் தொடங்குக. இது வைஃபை க்ளையண்ட்/ஹாட்ஸ்பாட்டை முடக்கும்."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS கோரிக்கையானது DIAL கோரிக்கைக்கு மாற்றப்பட்டது."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS கோரிக்கையானது USSD கோரிக்கைக்கு மாற்றப்பட்டது."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS கோரிக்கையானது புதிய SS கோரிக்கைக்கு மாற்றப்பட்டது."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB பெரிபெரல் போர்ட்"</string>
 </resources>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 9975c71..27eeb88 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 గంట క్రితం"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> గంటల క్రితం"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"గత <xliff:g id="COUNT">%d</xliff:g> రోజులు"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"గత నెల"</string>
     <string name="older" msgid="5211975022815554840">"పాతది"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android ప్రారంభమవుతోంది…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"నిల్వను అనుకూలపరుస్తోంది."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_1">%2$d</xliff:g>లో <xliff:g id="NUMBER_0">%1$d</xliff:g> అనువర్తనాన్ని అనుకూలీకరిస్తోంది."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"అనువర్తనాలను ప్రారంభిస్తోంది."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"బూట్‌ను ముగిస్తోంది."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> అమలవుతోంది"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fiకి కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" బలహీన ఇంటర్నెట్ కనెక్షన్‌ను కలిగి ఉంది."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"కనెక్షన్‌ని అనుమతించాలా?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s %2$sకి కనెక్ట్ చేయాలనుకుంటున్నారు"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s అనువర్తనం %2$s Wifi నెట్‌వర్క్‌కు కనెక్ట్ చేయాలనుకుంటోంది"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"ఒక అనువర్తనం"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Directను ప్రారంభించండి. దీని వలన Wi-Fi క్లయింట్/హాట్‌స్పాట్ ఆపివేయబడుతుంది."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS అభ్యర్థన డయల్ అభ్యర్థనగా సవరించబడింది."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS అభ్యర్థన USSD అభ్యర్థనగా సవరించబడింది."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS అభ్యర్థన కొత్త SS అభ్యర్థనగా సవరించబడింది."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB పెరిఫెరాల్ పోర్ట్"</string>
 </resources>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
new file mode 100644
index 0000000..3435474
--- /dev/null
+++ b/core/res/res/values-television/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for TV products.  Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Flags enabling default window features. See Window.java -->
+    <bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
+</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index f9bcd79..a4ebcec 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 ชั่วโมงที่ผ่านมา"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> ชั่วโมงที่ผ่านมา"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> วันที่แล้ว"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"เดือนที่แล้ว"</string>
     <string name="older" msgid="5211975022815554840">"เก่ากว่า"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android กำลังเริ่มต้น…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"กำลังเพิ่มประสิทธิภาพพื้นที่จัดเก็บข้อมูล"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"กำลังเพิ่มประสิทธิภาพแอปพลิเคชัน <xliff:g id="NUMBER_0">%1$d</xliff:g> จาก <xliff:g id="NUMBER_1">%2$d</xliff:g> รายการ"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"กำลังเตรียม <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"เสร็จสิ้นการบูต"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> กำลังทำงาน"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"ไม่สามารถเชื่อมต่อ WiFi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" มีสัญญาณอินเทอร์เน็ตไม่ดี"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"อนุญาตการเชื่อมต่อใช่ไหม"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s ต้องการเชื่อมต่อ %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"แอปพลิเคชัน %1$s ต้องการเชื่อมต่อเครือข่าย Wi-Fi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"แอปพลิเคชัน"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"WiFi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"เริ่มการทำงาน WiFi Direct ซึ่งจะเป็นการปิดการทำงาน WiFi ไคลเอ็นต์/ฮอตสปอต"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ DIAL"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ USSD"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ SS ใหม่"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"อุปกรณ์สำหรับต่อพอร์ต USB"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 2364e29..518d1f5 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 oras ang nakalipas"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> (na) oras ang nakalipas"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Huling <xliff:g id="COUNT">%d</xliff:g> (na) araw"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Nakaraang buwan"</string>
     <string name="older" msgid="5211975022815554840">"Mas luma"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Nagsisimula ang Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ino-optimize ang storage."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Ino-optimize ang app <xliff:g id="NUMBER_0">%1$d</xliff:g> ng <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Ihinahanda ang <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Sinisimulan ang apps."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Pagtatapos ng pag-boot."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Tumatakbo ang <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Hindi makakonekta sa Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ay mayroong mahinang koneksyon sa Internet."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Payagan ang kuneksyon?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"gustong kumonekta ni %1$s sa %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Gustong kumonekta ng application na %1$s sa Wifi Network na %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Isang application"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Simulan ang Wi-Fi Direct. I-o-off nito ang client/hotspot ng Wi-Fi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Ginawang DIAL request ang SS request."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Ginawang USSD request ang SS request."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Ginawang bagong SS request ang SS request."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB Peripheral Port"</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 07d1ba7..e62423e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -55,7 +55,7 @@
     <string name="mmiComplete" msgid="8232527495411698359">"MMI tamamlandı."</string>
     <string name="badPin" msgid="9015277645546710014">"Yazdığınız eski PIN doğru değil."</string>
     <string name="badPuk" msgid="5487257647081132201">"Yazdığınız PUK doğru değil."</string>
-    <string name="mismatchPin" msgid="609379054496863419">"Girdiğiniz PIN kodları eşleşmiyor"</string>
+    <string name="mismatchPin" msgid="609379054496863419">"Girdiğiniz PIN\'ler eşleşmiyor"</string>
     <string name="invalidPin" msgid="3850018445187475377">"4 ila 8 rakamdan oluşan bir PIN girin."</string>
     <string name="invalidPuk" msgid="8761456210898036513">"8 veya daha uzun basamaklı bir PUK kodu yazın."</string>
     <string name="needPuk" msgid="919668385956251611">"SIM kartınızın PUK kilidi devrede. Kilidi açmak için PUK kodunu yazın."</string>
@@ -192,7 +192,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Kapat"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hata raporu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Hata raporu al"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"Bu rapor, e-posta iletisi olarak göndermek üzere mevcut cihazınızın durumuyla ilgili bilgi toplar. Hata raporu başlatıldıktan sonra hazır olması biraz zaman alabilir, lütfen sabırlı olun."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"Bu rapor, e-posta iletisi olarak göndermek üzere cihazınızın şu anki durumuyla ilgili bilgi toplar. Hata raporu başlatıldıktan sonra hazır olması biraz zaman alabilir, lütfen sabırlı olun."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Sessiz mod"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Ses KAPALI"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Ses AÇIK"</string>
@@ -208,8 +208,8 @@
     <string name="managed_profile_label" msgid="6260850669674791528">"İş"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Size maliyet getiren hizmetler"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Size maliyet getirebilecek işlemler yapma."</string>
-    <string name="permgrouplab_messages" msgid="7521249148445456662">"Mesajlarınız"</string>
-    <string name="permgroupdesc_messages" msgid="7821999071003699236">"SMS mesajlarınızı, e-posta iletilerinizi ve diğer mesajlarınızı okuyup yazma."</string>
+    <string name="permgrouplab_messages" msgid="7521249148445456662">"İletileriniz"</string>
+    <string name="permgroupdesc_messages" msgid="7821999071003699236">"SMS, e-posta ve diğer iletilerinizi okuyup yazma."</string>
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"Kişisel bilgileriniz"</string>
     <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"Sizinle ilgili, kişi kartınızda kayıtlı bilgilere doğrudan erişim."</string>
     <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"Sosyal bilgileriniz"</string>
@@ -269,13 +269,13 @@
     <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"SD karta erişin."</string>
     <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Erişilebilirlik özellikleri"</string>
     <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Engelli kullanıcılara yardımcı olan teknolojinin istekte bulunabileceği özellikler."</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Pencere içeriğini alın"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Etkileşim kurduğunuz pencerenin içeriğini inceleyin."</string>
-    <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Dokunarak Keşfet\'i açın"</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Pencere içeriğini alma"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Etkileşim kurduğunuz pencerenin içeriğini inceler."</string>
+    <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Dokunarak Keşfet\'i açma"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"Dokunulan öğeler sesli olarak okunur ve ekranı keşfetmek için hareketler kullanılabilir."</string>
-    <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Gelişmiş web erişilebilirliğini açın"</string>
+    <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Gelişmiş web erişilebilirliğini açma"</string>
     <string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Uygulamanın erişilebilirliğini artırmak için komut dosyaları yüklenebilir."</string>
-    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Yazdığınız metni izleyin"</string>
+    <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Yazdığınız metni izleme"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredi kartı ve şifre gibi kişisel bilgiler içerir."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string>
@@ -290,25 +290,25 @@
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"giden çağrıları yeniden yönlendir"</string>
     <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Uygulamaya, giden bir çağrının numarası çevrilirken çağrıyı farklı bir numaraya yönlendirme ya da tamamen kapatma seçeneğiyle birlikte numarayı görme izni verir."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"kısa mesajları al (SMS)"</string>
-    <string name="permdesc_receiveSms" msgid="6424387754228766939">"Uygulamaya SMS mesajlarını alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen mesajları takip edip size göstermeden silebileceği anlamına gelir."</string>
+    <string name="permdesc_receiveSms" msgid="6424387754228766939">"Uygulamaya SMS iletilerini alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen iletileri takip edip size göstermeden silebileceği anlamına gelir."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"kısa mesajları (MMS) al"</string>
-    <string name="permdesc_receiveMms" msgid="533019437263212260">"Uygulamaya MMS mesajlarını alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen mesajları takip edip size göstermeden silebileceği anlamına gelir."</string>
+    <string name="permdesc_receiveMms" msgid="533019437263212260">"Uygulamaya MMS iletilerini alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen iletileri takip edip size göstermeden silebileceği anlamına gelir."</string>
     <string name="permlab_receiveEmergencyBroadcast" msgid="1803477660846288089">"acil durum yayınlarını al"</string>
     <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Uygulamaya, acil yayın mesajları alma ve işleme izni verir. Bu izin, sadece sistem uygulamaları için kullanılabilir."</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"hücre yayını mesajlarını oku"</string>
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Uygulamaya, cihazınız tarafından alınan hücre yayını mesajlarını okuma izni verir. Hücre yayını uyarıları bazı yerlerde acil durumlar konusunda sizi uyarmak için gönderilir. Kötü amaçlı uygulamalar acil hücre yayını alındığında cihazınızın performansına ya da çalışmasına engel olabilir."</string>
-    <string name="permlab_sendSms" msgid="5600830612147671529">"SMS mesajları gönder"</string>
-    <string name="permdesc_sendSms" msgid="7094729298204937667">"Uygulamaya SMS mesajları gönderme izni verir. Bu durum beklenmeyen ödemelere neden olabilir. Kötü amaçlı uygulamalar onayınız olmadan mesajlar göndererek sizi zarara uğratabilir."</string>
+    <string name="permlab_sendSms" msgid="5600830612147671529">"SMS iletileri gönder"</string>
+    <string name="permdesc_sendSms" msgid="7094729298204937667">"Uygulamaya SMS iletisi gönderme izni verir. Bu durum beklenmeyen ödemelere neden olabilir. Kötü amaçlı uygulamalar onayınız olmadan iletiler göndererek sizi zarara uğratabilir."</string>
     <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"mesajla yanıtla etkinlikleri gönder"</string>
     <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Uygulamaya, gelen çağrıları mesajla yanıtlama etkinliklerini işlemek üzere diğer mesajlaşma uygulamalarına istek gönderme izni verir."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"kısa mesajlarımı (SMS veya MMS) oku"</string>
-    <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Uygulamaya tabletinizde veya SIM kartta saklanan SMS mesajlarını okuma izni verir. Bu izin, uygulamanın tüm SMS mesajlarını içeriğinden veya gizliliğinden bağımsız olarak okumasına olanak sağlar."</string>
-    <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Uygulamaya, TV\'nizde veya SIM kartta depolanmış SMS mesajlarını okuma izni verir. Bu izin, uygulamanın içeriğe veya gizliliğe bakılmaksızın tüm SMS mesajlarını okumasına olanak sağlar."</string>
-    <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Uygulamaya telefonunuzda veya SIM kartta saklanan SMS mesajlarını okuma izni verir. Bu izin, uygulamanın tüm SMS mesajlarını içeriğinden veya gizliliğinden bağımsız olarak okumasına olanak sağlar."</string>
+    <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Uygulamaya tabletinizde veya SIM kartta saklanan SMS iletilerini okuma izni verir. Bu izin, uygulamanın tüm SMS iletilerini içeriğinden veya gizliliğinden bağımsız olarak okumasına olanak sağlar."</string>
+    <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Uygulamaya, TV\'nizde veya SIM kartta depolanmış SMS iletilerini okuma izni verir. Bu izin, uygulamanın içeriğe veya gizliliğe bakılmaksızın tüm SMS iletilerini okumasına olanak sağlar."</string>
+    <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Uygulamaya telefonunuzda veya SIM kartta saklanan SMS iletilerini okuma izni verir. Bu izin, uygulamanın tüm SMS iletilerini içeriğinden veya gizliliğinden bağımsız olarak okumasına olanak sağlar."</string>
     <string name="permlab_writeSms" msgid="3216950472636214774">"kısa mesajlarımı (SMS veya MMS) düzenle"</string>
-    <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"Uygulamaya, tabletinizde veya SIM kartınızda depolanan SMS mesajlarına yazma izni verir. Kötü amaçlı uygulamalar mesajlarınızı silebilir."</string>
-    <string name="permdesc_writeSms" product="tv" msgid="955871498983538187">"Uygulamaya, TV\'niz ya da SIM kartınızda saklanan SMS mesajlarına yazma izni verir. Kötü amaçlı uygulamalar mesajlarınızı silebilir."</string>
-    <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"Uygulamaya, telefonunuzdaki veya SIM kartınızdaki SMS mesajlarına yazma izni verir. Kötü amaçlı uygulamalar mesajlarınızı silebilir."</string>
+    <string name="permdesc_writeSms" product="tablet" msgid="5160413947794501538">"Uygulamaya, tabletinizde veya SIM kartınızda depolanan SMS iletilerine yazma izni verir. Kötü amaçlı uygulamalar iletilerinizi silebilir."</string>
+    <string name="permdesc_writeSms" product="tv" msgid="955871498983538187">"Uygulamaya, TV\'niz ya da SIM kartınızda saklanan SMS iletilerine yazma izni verir. Kötü amaçlı uygulamalar iletilerinizi silebilir."</string>
+    <string name="permdesc_writeSms" product="default" msgid="7268668709052328567">"Uygulamaya, telefonunuzdaki veya SIM kartınızdaki SMS iletilerine yazma izni verir. Kötü amaçlı uygulamalar iletilerinizi silebilir."</string>
     <string name="permlab_receiveWapPush" msgid="5991398711936590410">"kısa mesajları (WAP) al"</string>
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Uygulamaya WAP mesajlarını alma ve işleme izni verir. Buna, size gönderilen mesajları takip edip size göstermeden silebilme izni de dahildir."</string>
     <string name="permlab_receiveBluetoothMap" msgid="7593811487142360528">"Bluetooth iletilerini al (MAP)"</string>
@@ -370,7 +370,7 @@
     <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"paket ile kaldırılan yayını gönder"</string>
     <string name="permdesc_broadcastPackageRemoved" msgid="6621901216207931089">"Uygulamaya, bir uygulama paketinin kaldırıldığına dair bildirim yayınlama izni verir. Kötü amaçlı uygulamalar çalışan diğer uygulamaları kapatmak için bunu kullanabilir."</string>
     <string name="permlab_broadcastSmsReceived" msgid="5689095009030336593">"SMS ile alınan yayın gönder"</string>
-    <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Uygulamaya, SMS mesajı alındığına dair bildirim yayınlama izni verir. Kötü amaçlı uygulamalar sahte SMS mesajları göndermek için bunu kullanabilir."</string>
+    <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Uygulamaya, SMS iletisi alındığına dair bildirim yayınlama izni verir. Kötü amaçlı uygulamalar sahte SMS iletileri göndermek için bunu kullanabilir."</string>
     <string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH ile alınan yayın gönder"</string>
     <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Uygulamaya, WAP PUSH mesajı alındığına dair bildirim yayınlama izni verir. Kötü amaçlı uygulamalar sahte MMS bildirimleri oluşturmak veya bir web sayfasının içeriğini sessiz şekilde zararlı öğelerle değiştirmek için bunu kullanabilir."</string>
     <string name="permlab_setProcessLimit" msgid="2451873664363662666">"çalışan işlem sayısını sınırla"</string>
@@ -526,15 +526,15 @@
     <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"sosyal akışınızı okuma"</string>
     <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Uygulamaya size veya arkadaşlarınıza ait sosyal güncellemelere erişme ve bunları senkronize etme izni verir. Bilgi paylaşırken dikkatli olun. Bu izin, uygulamanın sosyal ağlarda sizinle arkadaşlarınız arasındaki iletişimi, gizliliğine bakılmaksızın okumasına olanak sağlar. Not: Bu izin tüm sosyal ağlar için geçerli olmayabilir."</string>
     <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"sosyal akışınıza yazma"</string>
-    <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Uygulamaya arkadaşlarınızın sosyal güncellemelerini gösterme izni verir. Bilgi paylaşırken dikkatli olun -- Bu uygulama bir arkadaşınızdan geliyormuş gibi görünen mesajlar oluşturabilir. Not: Bu izin, tüm sosyal ağlarda geçerli olmayabilir."</string>
+    <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Uygulamaya arkadaşlarınızın sosyal güncellemelerini gösterme izni verir. Bilgi paylaşırken dikkatli olun -- Bu uygulama bir arkadaşınızdan geliyormuş gibi görünen iletiler oluşturabilir. Not: Bu izin, tüm sosyal ağlarda geçerli olmayabilir."</string>
     <string name="permlab_readCalendar" msgid="5972727560257612398">"takvim etkinliklerini ve gizli bilgileri oku"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Uygulamaya, arkadaşlarınızın ve iş arkadaşlarınızın etkinlikleri de olmak üzere tabletinizde depolanan tüm takvim etkinliklerini okuma izni verir. Bu izin, uygulamanın takvim verilerinizi gizliliğine ve hassaslığına bakmaksızın paylaşmasına ve kaydetmesine olanak sağlayabilir."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Uygulamaya, arkadaşlarınızın ve iş arkadaşlarınızın etkinlikleri dahil olmak üzere TV\'nizde kayıtlı tüm takvim etkinliklerini okuma izni verir. Bu da uygulamanın gizlilik ve duyarlılık dikkate alınmaksızın takvim verilerinizi paylaşmasına veya kaydetmesine olanak sağlayabilir."</string>
     <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Uygulamaya, arkadaşlarınızın ve iş arkadaşlarınızın etkinlikleri de dahil olmak üzere telefonunuzda depolanan tüm takvim etkinliklerini okuma izni verir. Bu izin, uygulamanın takvim verilerinizi gizliliğine ve hassaslığına bakmaksızın paylaşmasına ve kaydetmesine olanak sağlayabilir."</string>
     <string name="permlab_writeCalendar" msgid="8438874755193825647">"sahibin bilgisi olmadan takvim etkinlikleri ekle veya mevcut etkinlikleri değiştir ve misafirlere e-posta gönder"</string>
-    <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Uygulamaya, arkadaşlarınızın veya iş arkadaşlarınızın etkinlikleri de dahil olmak üzere tabletinizde değiştirebileceğiniz etkinlikleri ekleme, kaldırma ve değiştirme izni verir. Bu izin, uygulamanın takvim sahiplerinden geliyormuş gibi görünen mesajlar göndermesine veya etkinlikleri sahibinden habersiz olarak değiştirmesine olanak sağlar."</string>
+    <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Uygulamaya, arkadaşlarınızın veya iş arkadaşlarınızın etkinlikleri de dahil olmak üzere tabletinizde değiştirebileceğiniz etkinlikleri ekleme, kaldırma ve değiştirme izni verir. Bu izin, uygulamanın takvim sahiplerinden geliyormuş gibi görünen iletiler göndermesine veya etkinlikleri sahibinden habersiz olarak değiştirmesine olanak sağlar."</string>
     <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Uygulamaya, arkadaşlarınızın veya iş arkadaşlarınızın etkinlikleri de dahil olmak üzere cihazınızda değiştirebileceğiniz etkinlikleri ekleme, kaldırma ve değiştirme izni verir. Bu izin, uygulamanın, takvim sahiplerinden gelmiş gibi görünen iletiler göndermesine veya takvim sahiplerinin bilgisi olmadan etkinlikleri değiştirmesine olanak sağlayabilir."</string>
-    <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Uygulamaya, arkadaşlarınızın veya iş arkadaşlarınızın etkinlikleri de dahil olmak üzere telefonunuzda değiştirebileceğiniz etkinlikleri ekleme, kaldırma ve değiştirme izni verir. Bu izin, uygulamanın takvim sahiplerinden geliyormuş gibi görünen mesajlar göndermesine veya etkinlikleri sahibinden habersiz olarak değiştirmesine olanak sağlar."</string>
+    <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Uygulamaya, arkadaşlarınızın veya iş arkadaşlarınızın etkinlikleri de dahil olmak üzere telefonunuzda değiştirebileceğiniz etkinlikleri ekleme, kaldırma ve değiştirme izni verir. Bu izin, uygulamanın takvim sahiplerinden geliyormuş gibi görünen iletiler göndermesine veya etkinlikleri sahibinden habersiz olarak değiştirmesine olanak sağlar."</string>
     <string name="permlab_accessMockLocation" msgid="8688334974036823330">"test için sahte konum kaynakları"</string>
     <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Test amacıyla veya yeni bir konum sağlayıcı yüklemek için sahte konum kaynakları oluşturma. Bu izin, uygulamanın GPS veya konum sağlayıcıları gibi diğer konum kaynakları tarafından döndürülen konum ve/veya durum bilgisini geçersiz kılmasına olanak sağlar."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ek konum sağlayıcı komutlarına eriş"</string>
@@ -747,9 +747,9 @@
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Uygulamaya, o anda senkronize olan özet akışları ile ilgili bilgi alma izni verir."</string>
     <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"abone olunan yayınları yazma"</string>
     <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"Uygulamaya, o anda senkronize edilmiş özet akışlarını değiştirme izni verir. Kötü amaçlı uygulamalar senkronize edilmiş özet akışlarını değiştirebilir."</string>
-    <string name="permlab_readDictionary" msgid="4107101525746035718">"sözlüğe eklediğim terimleri oku"</string>
+    <string name="permlab_readDictionary" msgid="4107101525746035718">"sözlüğe eklediğiniz terimleri okuma"</string>
     <string name="permdesc_readDictionary" msgid="659614600338904243">"Uygulamaya, kullanıcının kullanıcı sözlüğünde depolamış olabileceği kelimeleri, adları ve kelime öbeklerini okuma izni verir."</string>
-    <string name="permlab_writeDictionary" msgid="2183110402314441106">"kullanıcı tanımlı sözlüğe kelime ekle"</string>
+    <string name="permlab_writeDictionary" msgid="2183110402314441106">"kullanıcı tanımlı sözlüğe kelime ekleme"</string>
     <string name="permdesc_writeDictionary" msgid="8185385716255065291">"Uygulamaya, kullanıcı sözlüğüne yeni kelimeler yazma izni verir."</string>
     <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"USB belleğini okuma"</string>
     <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"SD kartımın içeriğini oku"</string>
@@ -817,11 +817,11 @@
     <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Ekran kilidini açarken yapılan yanlış şifre girme denemelerini izle ve çok fazla sayıda yanlış şifre girme denemesi yapılmışsa tableti kilitle veya tabletteki tüm verileri sil."</string>
     <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Ekran kilidi açılırken girilen hatalı şifre sayısını takip etme ve çok fazla sayıda hatalı şifre girildiğinde TV\'yi kilitleme veya TV\'nin tüm verilerini silme."</string>
     <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Ekran kilidini açarken yapılan yanlış şifre girişi denemelerini izle ve çok sayıda yanlış şifre girişi denemesi yapılmışsa telefonu kilitle veya telefonun tüm verilerini sil."</string>
-    <string name="policylab_resetPassword" msgid="2620077191242688955">"Ekran kilidini açma şifresini değiştir"</string>
+    <string name="policylab_resetPassword" msgid="2620077191242688955">"Ekran kilidini açma şifresini değiştirme"</string>
     <string name="policydesc_resetPassword" msgid="605963962301904458">"Ekran kilidini açma şifresini değiştirme."</string>
-    <string name="policylab_forceLock" msgid="2274085384704248431">"Ekranı kilitle"</string>
+    <string name="policylab_forceLock" msgid="2274085384704248431">"Ekranı kilitleme"</string>
     <string name="policydesc_forceLock" msgid="1141797588403827138">"Ekranın nasıl ve ne zaman kilitlendiğini denetleme."</string>
-    <string name="policylab_wipeData" msgid="3910545446758639713">"Tüm verileri sil"</string>
+    <string name="policylab_wipeData" msgid="3910545446758639713">"Tüm verileri silme"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek tabletteki verileri uyarıda bulunmadan silme."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Fabrika ayarlarına sıfırlama yoluyla TV\'nin verilerini uyarı vermeksizin silme."</string>
     <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek telefondaki verileri uyarıda bulunmadan silme."</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 saat önce"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> saat önce"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Son <xliff:g id="COUNT">%d</xliff:g> gün"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Son ay"</string>
     <string name="older" msgid="5211975022815554840">"Daha eski"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android başlatılıyor…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Depolama optimize ediliyor."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> uygulama optimize ediliyor."</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Uygulamalar başlatılıyor"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Açılış tamamlanıyor."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
@@ -1348,12 +1348,13 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kablosuz bağlantısı kurulamadı"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" İnternet bağlantısı zayıf."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Bağlantıya izin verilsin mi?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s, %2$s ile bağlantı kurmak istiyor"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Bir uygulama"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Kablosuz Doğrudan Bağlantı"</string>
-    <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Kablosuz Doğrudan Bağlantıyı başlat. Bu işlem, Kablosuz istemci/hotspot kullanımını kapatacak."</string>
-    <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kablosuz Doğrudan bağlantı başlatılamadı."</string>
-    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Kablosuz Doğrudan özelliği açık"</string>
+    <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Kablosuz Doğrudan Bağlantı\'yı başlat. Bu işlem, Kablosuz istemci/hotspot kullanımını kapatacak."</string>
+    <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kablosuz Doğrudan Bağlantı başlatılamadı."</string>
+    <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Kablosuz Doğrudan Bağlantı özelliği açık"</string>
     <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Ayarlar için dokunun"</string>
     <string name="accept" msgid="1645267259272829559">"Kabul et"</string>
     <string name="decline" msgid="2112225451706137894">"Reddet"</string>
@@ -1367,11 +1368,11 @@
     <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ile bağlantıya sahipken TV\'nizin Kablosuz bağlantısı geçici olarak kesilecek."</string>
     <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter ekle"</string>
-    <string name="sms_control_title" msgid="7296612781128917719">"SMS mesajları gönderiliyor"</string>
-    <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; çok sayıda SMS mesajı gönderiyor. Bu uygulamanın mesaj göndermeye devam etmesine izin veriyor musunuz?"</string>
+    <string name="sms_control_title" msgid="7296612781128917719">"SMS iletileri gönderiliyor"</string>
+    <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; çok sayıda SMS iletisi gönderiyor. Bu uygulamanın ileti göndermeye devam etmesine izin veriyor musunuz?"</string>
     <string name="sms_control_yes" msgid="3663725993855816807">"İzin ver"</string>
     <string name="sms_control_no" msgid="625438561395534982">"Reddet"</string>
-    <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;, &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; adresine bir mesaj göndermek istiyor."</string>
+    <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;, &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; adresine bir ileti göndermek istiyor."</string>
     <string name="sms_short_code_details" msgid="5873295990846059400">"Bu işlem, mobil hesabınızdan "<b>"ödeme alınmasına neden olabilir"</b>"."</string>
     <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Bu işlem, mobil hesabınızdan ödeme alınmasına neden olacak."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Gönder"</string>
@@ -1524,7 +1525,7 @@
     <string name="submit" msgid="1602335572089911941">"Gönder"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Araba modu etkin"</string>
     <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"Araba modundan çıkmak için dokunun."</string>
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Doğrudan bağlantı veya ortak erişim noktası etkin"</string>
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering veya hotspot etkin"</string>
     <string name="tethered_notification_message" msgid="6857031760103062982">"Kurulum için dokunun."</string>
     <string name="back_button_label" msgid="2300470004503343439">"Geri"</string>
     <string name="next_button_label" msgid="1080555104677992408">"İleri"</string>
@@ -1847,8 +1848,8 @@
     <string name="lock_to_app_unlock_pattern" msgid="4182192144797225137">"Sabitlemeyi kaldırmadan önce kilit açma desenini sor"</string>
     <string name="lock_to_app_unlock_password" msgid="6380979775916974414">"Sabitlemeyi kaldırmadan önce şifre sor"</string>
     <string name="battery_saver_description" msgid="1960431123816253034">"Pil tasarrufu özelliği, pil ömrünü iyileştirmeye yardımcı olmak için cihazın performansını düşürür, titreşimi, konum hizmetlerini ve arka plan verilerinin çoğunu sınırlar. Senkronizasyona dayalı olarak çalışan e-posta, mesajlaşma uygulamaları ve diğer uygulamalar, bunları açmadığınız sürece güncellenmeyebilir.\n\nCihazınız şarj olurken pil tasarrufu otomatik olarak kapatılır."</string>
-    <string name="downtime_condition_summary" msgid="8761776337475705749">"Kesinti süreniz <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> saatinde sona erene kadar"</string>
-    <string name="downtime_condition_line_one" msgid="8762708714645352010">"Kullanım dışı kalma durumunuz bitene kadar"</string>
+    <string name="downtime_condition_summary" msgid="8761776337475705749">"Bildirim istenmeyen zaman <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> saatinde sona erene kadar"</string>
+    <string name="downtime_condition_line_one" msgid="8762708714645352010">"Bildirim istenmeyen zaman bitene kadar"</string>
   <plurals name="zen_mode_duration_minutes_summary">
     <item quantity="one" msgid="3177683545388923234">"Bir dakika için (şu saate kadar: <xliff:g id="FORMATTEDTIME">%2$s</xliff:g>)"</item>
     <item quantity="other" msgid="2787867221129368935">"%1$d dakika için (şu saate kadar: <xliff:g id="FORMATTEDTIME">%2$s</xliff:g>)"</item>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS isteği DIAL isteği olarak değiştirildi."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS isteği USSD isteği olarak değiştirildi."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS isteği yeni SS isteği olarak değiştirildi."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 79281b3..47c364b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -71,7 +71,7 @@
     <string name="ClirMmi" msgid="7784673673446833091">"Вихід. ід. абонента"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"Ідентифікатор під’єднаної лінії"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Обмеження ідентифікатора під’єднаної лінії"</string>
-    <string name="CfMmi" msgid="5123218989141573515">"Переадрес. виклику"</string>
+    <string name="CfMmi" msgid="5123218989141573515">"Переадресація виклику"</string>
     <string name="CwMmi" msgid="9129678056795016867">"Паралел. виклик"</string>
     <string name="BaMmi" msgid="455193067926770581">"Заборона викл."</string>
     <string name="PwdMmi" msgid="7043715687905254199">"Зміна пароля"</string>
@@ -879,9 +879,9 @@
     <item msgid="1648797903785279353">"Jabber"</item>
   </string-array>
     <string name="phoneTypeCustom" msgid="1644738059053355820">"Указати"</string>
-    <string name="phoneTypeHome" msgid="2570923463033985887">"Дом."</string>
+    <string name="phoneTypeHome" msgid="2570923463033985887">"Домашній"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"Мобільний"</string>
-    <string name="phoneTypeWork" msgid="8863939667059911633">"Роб."</string>
+    <string name="phoneTypeWork" msgid="8863939667059911633">"Робочий"</string>
     <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Роб. факс"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Дом. факс"</string>
     <string name="phoneTypePager" msgid="7582359955394921732">"Пейджер"</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 год. тому"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> год. тому"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Остан. <xliff:g id="COUNT">%d</xliff:g> дн."</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Останній міс."</string>
     <string name="older" msgid="5211975022815554840">"Давніше"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Запуск ОС Android…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Оптимізація пам’яті."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Оптимізація програми <xliff:g id="NUMBER_0">%1$d</xliff:g> з <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Підготовка додатка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Запуск програм."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Завершення завантаження."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"Працює <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не вдалося під’єднатися до мережі Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" має погане з’єднання з Інтернетом."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Дозволити з’єднання?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s хоче під’єднатися до %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Додаток %1$s хоче під’єднатися до мережі Wi-Fi \"%2$s\""</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Додаток"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Запустити Wi-Fi Direct. Це вимкне з’єднання Wi-Fi клієнт/точка доступу."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Запит SS перетворено на запит DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Запит SS перетворено на запит USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Запит SS перетворено на новий запит SS."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Периферійний USB-порт"</string>
 </resources>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 7f8a88e..ae7acf2 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 گھنٹہ پہلے"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> گھنٹے پہلے"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"آخری <xliff:g id="COUNT">%d</xliff:g> دن"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"پچھلے مہینے"</string>
     <string name="older" msgid="5211975022815554840">"پرانا"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"‏Android شروع ہو رہا ہے…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"اسٹوریج کو بہترین بنایا جا رہا ہے۔"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"ایپ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g> کو بہتر بنایا جا رہا ہے۔"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> تیار ہو رہی ہے۔"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"ایپس شروع ہو رہی ہیں۔"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"بوٹ مکمل ہو رہا ہے۔"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> چل رہی ہے"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"‏Wi-Fi سے مربوط نہیں ہو سکا"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" اس میں ایک کمزور انٹرنیٹ کنکشن ہے۔"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"کنکشن کی اجازت دیں؟"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"‏%2$s سے ‎%1$s‎ منسلک ہونا چاہے گا"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"‏ایپلیکیشن ‎%1$s Wifi نیٹ ورک ‎%2$s سے منسلک ہونا چاہتی ہے"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"ایک ایپلیکیشن"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"‏Wi-Fi ڈائریکٹ"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"‏Wi-Fi ڈائرکٹ شروع کریں۔ یہ Wi-Fi کلائنٹ/ہاٹ اسپاٹ کو آف کردے گا۔"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"‏SS درخواست میں ترمیم کر کے DIAL درخواست بنا دی گئی ہے۔"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"‏SS درخواست میں ترمیم کر کے USSD درخواست بنا دی گئی ہے۔"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"‏SS درخواست میں ترمیم کر کے نئی SS درخواست بنا دی گئی ہے۔"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"‏USB ملحقہ پورٹ"</string>
 </resources>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 306e4b2..51d24ed 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 soat oldin"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> soat oldin"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"O‘tgan <xliff:g id="COUNT">%d</xliff:g> kun"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"O‘tgan oy"</string>
     <string name="older" msgid="5211975022815554840">"Eskiroq"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android ishga tushmoqda…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Xotira optimallashtirilmoqda."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Ilovalar optimallashtirilmoqda (<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>)."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> tayyorlanmoqda."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Ilovalar ishga tushirilmoqda."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Tizimni yuklashni tugatish."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> ishlamoqda"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi’ga ulana olmadi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tezligi past Internetga ulangan."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Ulanishga ruxsat berilsinmi?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s quyidagiga ulanmoqchi: %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"%1$s ilovasi %2$s Wi-Fi tarmog‘iga ulanmoqchi"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Ilova"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct’ni ishga tushirish. Bu Wi-Fi mijoz/ulanish nuqtasini o‘chiradi."</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS so‘rovi DIAL so‘roviga o‘zgartirildi."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS so‘rovi USSD so‘roviga o‘zgartirildi."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS so‘rovi yangi SS so‘roviga o‘zgartirildi."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Tashqi USB porti"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e361bad..a6172d7 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 giờ trước"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> giờ trước"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"<xliff:g id="COUNT">%d</xliff:g> ngày trước"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Tháng trước"</string>
     <string name="older" msgid="5211975022815554840">"Cũ hơn"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android đang khởi động..."</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Tối ưu hóa lưu trữ."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Đang tối ưu hóa ứng dụng <xliff:g id="NUMBER_0">%1$d</xliff:g> trong tổng số <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"Đang chuẩn bị <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Khởi động ứng dụng."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Hoàn tất khởi động."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> đang hoạt động"</string>
@@ -1348,9 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Không thể kết nối với Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" có kết nối Internet không tốt."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Cho phép kết nối?"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for wifi_connect_alert_message (8930084523889618078) -->
-    <skip />
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"Ứng dụng %1$s muốn kết nối với Mạng Wifi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Ứng dụng"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Khởi động Wi-Fi Direct. Việc này sẽ tắt hoạt động của ứng dụng khách/điểm phát sóng Wi-Fi."</string>
@@ -1881,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Yêu cầu SS được sửa đổi thành yêu cầu DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Yêu cầu SS được sửa đổi thành yêu cầu USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Yêu cầu SS được sửa đổi thành yêu cầu SS mới."</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"Cổng ngoại vi USB"</string>
 </resources>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 3eede32..307a1ea 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -23,7 +23,7 @@
 
     <!-- Only show settings item due to smaller real estate. -->
     <string-array translatable="false" name="config_globalActionsList">
-        <item>settings</item>
+        <item>voiceassist</item>
     </string-array>
 
     <!-- Base "touch slop" value used by ViewConfiguration as a
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4a03e7b..d225e3f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -970,7 +970,7 @@
     <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"重试"</string>
     <string name="lockscreen_password_wrong" msgid="5737815393253165301">"重试"</string>
     <string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超过“人脸解锁”尝试次数上限"</string>
-    <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有SIM卡"</string>
+    <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"没有 SIM 卡"</string>
     <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板电脑中没有SIM卡。"</string>
     <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"电视中没有 SIM 卡。"</string>
     <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"手机中无SIM卡"</string>
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1小时前"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g>小时前"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"过去<xliff:g id="COUNT">%d</xliff:g>天"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"上个月"</string>
     <string name="older" msgid="5211975022815554840">"往前"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android 正在启动…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在优化存储空间。"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"正在优化第<xliff:g id="NUMBER_0">%1$d</xliff:g>个应用(共<xliff:g id="NUMBER_1">%2$d</xliff:g>个)。"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在启动应用。"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"即将完成启动。"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"无法连接到WLAN"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 互联网连接状况不佳。"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"要允许连接吗?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s想要连接到%2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"一款应用"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"WLAN直连"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"启动WLAN直连。此操作将会关闭WLAN客户端/热点。"</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 请求已修改为 DIAL 请求。"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 请求已修改为 USSD 请求。"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 请求已修改为新的 SS 请求。"</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bb39491..210e955 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 小時前"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"最近 <xliff:g id="COUNT">%d</xliff:g> 天"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"上個月"</string>
     <string name="older" msgid="5211975022815554840">"較舊"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android 正在啟動…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在優化儲存空間。"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"正在優化第 <xliff:g id="NUMBER_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="NUMBER_1">%2$d</xliff:g> 個)。"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在啟動應用程式。"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"啟動完成。"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"正在執行 <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"無法連線至 Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 互聯網連線欠佳。"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"允許連線?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s 要求連線至 %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"「%1$s」應用程式要求連線至 WiFi 網路 %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"應用程式"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"啟動 Wi-Fi Direct,這會關閉 Wi-Fi 使用者端/熱點。"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 要求已修改為 DIAL 要求。"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 要求已修改為 USSD 要求。"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 要求已修改為新的 SS 要求。"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB 週邊連接埠"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 507205a..97d878a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 小時以前"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> 小時前"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"最近 <xliff:g id="COUNT">%d</xliff:g> 天"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"上個月"</string>
     <string name="older" msgid="5211975022815554840">"較舊"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,7 @@
     <string name="android_start_title" msgid="8418054686415318207">"Android 正在啟動…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"正在對儲存空間進行最佳化處理。"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"正在最佳化第 <xliff:g id="NUMBER_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="NUMBER_1">%2$d</xliff:g> 個)。"</string>
+    <string name="android_preparing_apk" msgid="8162599310274079154">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"正在啟動應用程式。"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"啟動完成。"</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
@@ -1348,7 +1347,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"無法連線至 Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 的網際網路連線狀況不佳。"</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"允許連線?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"%1$s 要求連線至 %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="6451273376815958922">"「%1$s」應用程式要求連線至 WiFi 網路 %2$s"</string>
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"應用程式"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"啟動 Wi-Fi Direct 作業,這會關閉 Wi-Fi 用戶端/無線基地台作業。"</string>
@@ -1879,4 +1878,6 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 要求已改為 DIAL 要求。"</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 要求已改為 USSD 要求。"</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 要求已改為新的 SS 要求。"</string>
+    <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
+    <string name="usb_midi_peripheral_model_name" msgid="1959288763942653301">"USB 週邊連接埠"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 1222904..f9eb830 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1142,9 +1142,7 @@
     <item quantity="one" msgid="9150797944610821849">"1 ihora eledlule"</item>
     <item quantity="other" msgid="2467273239587587569">"<xliff:g id="COUNT">%d</xliff:g> amahora adlule"</item>
   </plurals>
-  <plurals name="last_num_days">
-    <item quantity="other" msgid="3069992808164318268">"Izinsuku zokugcina ezingu- <xliff:g id="COUNT">%d</xliff:g>"</item>
-  </plurals>
+    <!-- no translation found for last_num_days:one (7555846096746489821) -->
     <string name="last_month" msgid="3959346739979055432">"Inyanga edlule"</string>
     <string name="older" msgid="5211975022815554840">"Okudala kakhulu"</string>
   <plurals name="num_days_ago">
@@ -1303,6 +1301,8 @@
     <string name="android_start_title" msgid="8418054686415318207">"I-Android iyaqala…"</string>
     <string name="android_upgrading_fstrim" msgid="8036718871534640010">"Ikhulisa isitoreji."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Ukubeka ezingeni eliphezulu <xliff:g id="NUMBER_0">%1$d</xliff:g> uhlelo lokusebenza <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+    <!-- no translation found for android_preparing_apk (8162599310274079154) -->
+    <skip />
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Qalisa izinhlelo zokusebenza."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Qedela ukuqala kabusha."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> iyasebenza"</string>
@@ -1348,7 +1348,8 @@
     <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ayikwazanga ukuxhuma kwi-Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" inoxhumano oluphansi lwe-inthanethi."</string>
     <string name="wifi_connect_alert_title" msgid="8455846016001810172">"Vumela ukuxhumeka?"</string>
-    <string name="wifi_connect_alert_message" msgid="8930084523889618078">"U-%1$s angathanda ukuxhumeka ku-%2$s"</string>
+    <!-- no translation found for wifi_connect_alert_message (6451273376815958922) -->
+    <skip />
     <string name="wifi_connect_default_application" msgid="7143109390475484319">"Uhlelo lokusebenza"</string>
     <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"I-Wi-Fi Eqondile"</string>
     <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Qala ukusebenza kwe-Wi-Fi Okuqondile. Lokhu kuzocima ikhasimende le-Wi-Fi/Ukusebenza okwe-hotspot"</string>
@@ -1879,4 +1880,8 @@
     <string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Isicelo se-SS siguqulelwe kusicelo se-DIAL."</string>
     <string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Isicelo se-SS siguqulelwe kusicelo se-USSD."</string>
     <string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Isicelo se-SS siguqulelwe kusicelo esisha se-SS."</string>
+    <!-- no translation found for usb_midi_peripheral_manufacturer_name (7176526170008970168) -->
+    <skip />
+    <!-- no translation found for usb_midi_peripheral_model_name (1959288763942653301) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 551c044..b90ac65 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1952,6 +1952,16 @@
 
         <!-- Whether to clip window content to the outline of the window background. -->
         <attr name="windowClipToOutline" format="boolean" />
+
+        <!-- If set, the status bar will be drawn such that it is compatible with a light
+             status bar background.
+             <p>For this to take effect, the window must be drawing the system bar backgrounds with
+             {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the status bar must not
+             have been requested to be translucent with
+             {@link android.R.attr#windowTranslucentStatus}.
+             Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_STATUS_BAR} on
+             the decor view. -->
+        <attr name="windowHasLightStatusBar" format="boolean" />
     </declare-styleable>
 
     <!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -2631,6 +2641,8 @@
     <declare-styleable name="Include">
         <attr name="id" />
         <attr name="visibility" />
+        <attr name="layout_width" />
+        <attr name="layout_height" />
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -4338,6 +4350,10 @@
         <attr name="popupAnimationStyle" format="reference" />
         <!-- Whether the popup window should overlap its anchor view. -->
         <attr name="overlapAnchor" format="boolean" />
+        <!-- Transition used to move views into the popup window. -->
+        <attr name="popupEnterTransition" format="reference" />
+        <!-- Transition used to move views out of the popup window. -->
+        <attr name="popupExitTransition" format="reference" />
     </declare-styleable>
     <declare-styleable name="ListPopupWindow">
         <!-- Amount of pixels by which the drop down should be offset vertically. -->
@@ -5074,14 +5090,31 @@
 
     <!-- Describes an item (or child) of a LayerDrawable. -->
     <declare-styleable name="LayerDrawableItem">
-        <!-- Left coordinate of the layer. -->
+        <!-- Left inset to apply to the layer. -->
         <attr name="left" />
-        <!-- Top coordinate of the layer. -->
+        <!-- Top inset to apply to the layer. -->
         <attr name="top" />
-        <!-- Right coordinate of the layer. -->
+        <!-- Right inset to apply to the layer. -->
         <attr name="right" />
-        <!-- Bottom coordinate of the layer. -->
+        <!-- Bottom inset to apply to the layer. -->
         <attr name="bottom" />
+        <!-- Start inset to apply to the layer. Overrides {@code left} or
+             {@code right} depending on layout direction. -->
+        <attr name="start" format="dimension" />
+        <!-- End inset to apply to the layer. Overrides {@code left} or
+             {@code right} depending on layout direction. -->
+        <attr name="end" format="dimension" />
+        <!-- Width of the layer. Defaults to the layer's intrinsic width. -->
+        <attr name="width" />
+        <!-- Height of the layer. Defaults to the layer's intrinsic height -->
+        <attr name="height" />
+        <!-- Gravity used to align the layer within its container. If no value
+             is specified, the default behavior depends on whether an explicit
+             width or height has been set, If no dimension is set, gravity in
+             that direction defaults to {@code fill_horizontal} or
+             {@code fill_vertical}; otherwise, it defaults to {@code left} or
+             {@code top}. -->
+        <attr name="gravity" />
         <!-- Drawable used to render the layer. -->
         <attr name="drawable" />
         <!-- Identifier of the layer. This can be used to retrieve the layer
@@ -5963,11 +5996,11 @@
              set when a view is enabled. -->
         <attr name="state_enabled" format="boolean" />
         <!-- State identifier indicating that the object <var>may</var> display a check mark.
-             See {@link R.attr#state_checked} for the identifier that indicates whether it is
+             See {@link android.R.attr#state_checked} for the identifier that indicates whether it is
              actually checked. -->
         <attr name="state_checkable" format="boolean"/>
         <!-- State identifier indicating that the object is currently checked.  See
-             {@link R.attr#state_checkable} for an additional identifier that can indicate if
+             {@link android.R.attr#state_checkable} for an additional identifier that can indicate if
              any object may ever display a check, regardless of whether state_checked is
              currently set. -->
         <attr name="state_checked" format="boolean"/>
@@ -7034,75 +7067,16 @@
     <!-- =============================== -->
     <eat-comment />
     <declare-styleable name="GlowPadView">
-        <!-- Reference to an array resource that be shown as targets around a circle. -->
-        <attr name="targetDrawables" format="reference" />
-
-        <!-- Reference to an array resource that be used as description for the targets around the circle. -->
+        <!-- Reference to an array resource that be used as description for the targets around the circle.
+             {@deprecated Removed.} -->
         <attr name="targetDescriptions" format="reference" />
 
-        <!-- Reference to an array resource that be used to announce the directions with targets around the circle. -->
+        <!-- Reference to an array resource that be used to announce the directions with targets around the circle.
+             {@deprecated Removed.}-->
         <attr name="directionDescriptions" format="reference" />
-
-        <!-- Sets a drawable as the center. -->
-        <attr name="handleDrawable" format="reference" />
-
-        <!-- Drawable to use for wave ripple animation. -->
-        <attr name="outerRingDrawable" format="reference"/>
-
-        <!-- Drawble used for drawing points -->
-        <attr name="pointDrawable" format="reference" />
-
-        <!-- Inner radius of glow area. -->
-        <attr name="innerRadius"/>
-
-        <!-- Outer radius of glow area. Target icons will be drawn on this circle. -->
-        <attr name="outerRadius" format="dimension" />
-
-        <!-- Radius of glow under finger. -->
-        <attr name="glowRadius" format="dimension" />
-
-        <!-- Tactile feedback duration for actions. Set to '0' for no vibration. -->
-        <attr name="vibrationDuration" format="integer" />
-
-        <!-- How close we need to be before snapping to a target. -->
-        <attr name="snapMargin" format="dimension" />
-
-        <!-- Number of waves/chevrons to show in animation. -->
-        <attr name="feedbackCount" format="integer" />
-
-        <!-- Used when the handle shouldn't wait to be hit before following the finger -->
-        <attr name="alwaysTrackFinger" format="boolean" />
-
-        <!-- Location along the circle of the first item, in degrees.-->
-        <attr name="firstItemOffset" format="float" />
-
-        <!-- Causes targets to snap to the finger location on activation. -->
-        <attr name="magneticTargets" format="boolean" />
-
-        <attr name="gravity" />
-
-        <!-- Determine whether the glow pad is allowed to scale to fit the bounds indicated
-            by its parent. If this is set to false, no scaling will occur. If this is set to true
-            scaling will occur to fit for any axis in which gravity is set to center. -->
-        <attr name="allowScaling" format="boolean" />
     </declare-styleable>
 
     <!-- =============================== -->
-    <!-- SizeAdaptiveLayout class attributes -->
-    <!-- =============================== -->
-    <eat-comment />
-    <declare-styleable name="SizeAdaptiveLayout_Layout">
-      <!-- The maximum valid height for this item. -->
-      <attr name="layout_maxHeight" format="dimension">
-        <!-- Indicates that the view may be resized arbitrarily large. -->
-        <enum name="unbounded" value="-1" />
-      </attr>
-      <!-- The minimum valid height for this item. -->
-      <attr name="layout_minHeight" format="dimension" />
-    </declare-styleable>
-    <declare-styleable name="SizeAdaptiveLayout" />
-
-    <!-- =============================== -->
     <!-- Location package class attributes -->
     <!-- =============================== -->
     <eat-comment />
@@ -7316,8 +7290,34 @@
     <declare-styleable name="Switch">
         <!-- Drawable to use as the "thumb" that switches back and forth. -->
         <attr name="thumb" />
+        <!-- Tint to apply to the thumb. -->
+        <attr name="thumbTint" />
+        <!-- Blending mode used to apply the thumb tint. -->
+        <attr name="thumbTintMode" />
         <!-- Drawable to use as the "track" that the switch thumb slides within. -->
         <attr name="track" format="reference" />
+        <!-- Tint to apply to the track. -->
+        <attr name="trackTint" format="color" />
+        <!-- Blending mode used to apply the track tint. -->
+        <attr name="trackTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
         <!-- Text to use when the switch is in the checked/"on" state. -->
         <attr name="textOn" />
         <!-- Text to use when the switch is in the unchecked/"off" state. -->
@@ -7509,11 +7509,6 @@
         <enum name="pageDeleteDropTarget" value="7" />
     </attr>
 
-    <declare-styleable name="SlidingChallengeLayout_Layout">
-        <attr name="layout_childType" />
-        <attr name="layout_maxHeight" />
-    </declare-styleable>
-
     <!-- Attributes that can be used with <code>&lt;FragmentBreadCrumbs&gt;</code>
     tags. -->
     <declare-styleable name="FragmentBreadCrumbs">
@@ -7522,27 +7517,6 @@
         <attr name="itemColor" format="color|reference" />
     </declare-styleable>
 
-    <declare-styleable name="MultiPaneChallengeLayout">
-        <!-- Influences how layout_centerWithinArea behaves -->
-        <attr name="orientation" />
-    </declare-styleable>
-
-    <declare-styleable name="MultiPaneChallengeLayout_Layout">
-        <!-- Percentage of the screen this child should consume or center within.
-             If 0/default, the view will be measured by standard rules
-             as if this were a FrameLayout. -->
-        <attr name="layout_centerWithinArea" format="float" />
-        <attr name="layout_childType" />
-        <attr name="layout_gravity" />
-        <attr name="layout_maxWidth" format="dimension" />
-        <attr name="layout_maxHeight" />
-    </declare-styleable>
-
-    <declare-styleable name="KeyguardSecurityViewFlipper_Layout">
-        <attr name="layout_maxWidth" />
-        <attr name="layout_maxHeight" />
-    </declare-styleable>
-
     <declare-styleable name="Toolbar">
         <attr name="titleTextAppearance" format="reference" />
         <attr name="subtitleTextAppearance" format="reference" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0c3fb9a..84609ca 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1012,6 +1012,20 @@
          <p>The default value of this attribute is <code>false</code>. -->
     <attr name="resumeWhilePausing" format="boolean" />
 
+    <!-- Indicates that it is okay for this activity to be resized to any dimension. Intended for a
+         multi-window device where there can be multiple activities of various sizes on the screen
+         at the same time.
+
+         <p>The default value is <code>false</code> for applications with
+         <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#MNC} and
+         <code>true</code> otherwise.
+
+         <p>NOTE: A task's root activity value is applied to all additional activities launched in
+         the task. That is if the root activity of a task is resizeable then the system will treat
+         all other activities in the task as resizeable and will not if the root activity isn't
+         resizeable. -->
+    <attr name="resizeableActivity" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1020,7 +1034,7 @@
          to avoid name collisions.  For example, applications published
          by Google could have names of the form
          <code>com.google.app.<em>appname</em></code>
-         
+
          <p>Inside of the manifest tag, may appear the following tags
          in any order: {@link #AndroidManifestPermission permission},
          {@link #AndroidManifestPermissionGroup permission-group},
@@ -1039,7 +1053,7 @@
         <attr name="sharedUserLabel" />
         <attr name="installLocation" />
     </declare-styleable>
-    
+
     <!-- The <code>application</code> tag describes application-level components
          contained in the package, as well as general application
          attributes.  Many of the attributes you can supply here (such
@@ -1047,7 +1061,7 @@
          and allowTaskReparenting) serve
          as default values for the corresponding attributes of components
          declared inside of the application.
-         
+
          <p>Inside of this element you specify what the application contains,
          using the elements {@link #AndroidManifestProvider provider},
          {@link #AndroidManifestService service},
@@ -1698,6 +1712,7 @@
         <attr name="autoRemoveFromRecents" />
         <attr name="relinquishTaskIdentity" />
         <attr name="resumeWhilePausing" />
+        <attr name="resizeableActivity" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 68c1046..f2d9de8 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -424,6 +424,10 @@
     <!-- Integer indicating wpa_supplicant scan interval in milliseconds -->
     <integer translatable="false" name="config_wifi_supplicant_scan_interval">15000</integer>
 
+    <!-- Integer indicating amount of time failed networks areblacklisted for the purpose
+         of network switching in milliseconds -->
+    <integer translatable="false" name="config_wifi_network_switching_blacklist_time">172800000</integer>
+
     <!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds -->
     <integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer>
 
@@ -2021,4 +2025,7 @@
 
     <!-- Use ERI text for network name on CDMA LTE -->
     <bool name="config_LTE_eri_for_network_name">true</bool>
+
+    <!-- Whether to start in touch mode -->
+    <bool name="config_defaultInTouchMode">true</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9f49b08..e2a0ec9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1759,7 +1759,9 @@
   <public type="attr" name="actionModeSplitBackground" id="0x0101039d" />
   <public type="attr" name="textAppearanceListItem" id="0x0101039e" />
   <public type="attr" name="textAppearanceListItemSmall" id="0x0101039f" />
+  <!-- @deprecated Removed. -->
   <public type="attr" name="targetDescriptions" id="0x010103a0" />
+  <!-- @deprecated Removed. -->
   <public type="attr" name="directionDescriptions" id="0x010103a1" />
   <public type="attr" name="overridesImplicitlyEnabledSubtype" id="0x010103a2" />
   <public type="attr" name="listPreferredItemPaddingLeft" id="0x010103a3" />
@@ -2606,6 +2608,13 @@
        =============================================================== -->
   <eat-comment />
 
+  <public type="attr" name="trackTint" />
+  <public type="attr" name="trackTintMode" />
+  <public type="attr" name="resizeableActivity" />
+  <public type="attr" name="start" />
+  <public type="attr" name="end" />
+  <public type="attr" name="windowHasLightStatusBar" />
+
   <public type="style" name="Widget.Material.Button.Colored" />
 
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f51e82c9..9946ce8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -482,6 +482,9 @@
     <!-- label for item that launches settings in phone options dialog [CHAR LIMIT=15]-->
     <string name="global_action_settings">Settings</string>
 
+    <!-- label for item that launches voice assist in phone options dialog [CHAR LIMIT=15]-->
+    <string name="global_action_voice_assist">Voice Assist</string>
+
     <!-- label for item that locks the phone and enforces that it can't be unlocked without entering a credential. [CHAR LIMIT=15] -->
     <string name="global_action_lockdown">Lock now</string>
 
@@ -3397,6 +3400,7 @@
 
     <!-- This is used to express that something occurred within the last X days (e.g., Last 7 days). -->
     <plurals name="last_num_days">
+        <item quantity="one">Last <xliff:g id="count">%d</xliff:g> day</item>
         <item quantity="other">Last <xliff:g id="count">%d</xliff:g> days</item>
     </plurals>
 
@@ -3748,6 +3752,9 @@
         <xliff:g id="number" example="123">%1$d</xliff:g> of
         <xliff:g id="number" example="123">%2$d</xliff:g>.</string>
 
+    <!-- [CHAR LIMIT=NONE] Message shown in upgrading dialog for each .apk pre boot broadcast -->
+    <string name="android_preparing_apk">Preparing <xliff:g id="appname">%1$s</xliff:g>.</string>
+
     <!-- [CHAR LIMIT=NONE] Message to show in upgrading dialog when reached the point of starting apps. -->
     <string name="android_upgrading_starting_apps">Starting apps.</string>
 
@@ -3853,7 +3860,7 @@
     <!-- title for this message -->
     <string name="wifi_connect_alert_title">Allow connection?</string>
     <!-- message explaining who is connecting to what -->
-    <string name="wifi_connect_alert_message">%1$s would like to connect to %2$s</string>
+    <string name="wifi_connect_alert_message">Application %1$s would like to connect to Wifi Network %2$s</string>
     <!-- default application in case name can not be found -->
     <string name="wifi_connect_default_application">An application</string>
 
@@ -5188,4 +5195,9 @@
     <string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
     <string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
 
+    <!-- Manufacturer name for USB MIDI Peripheral port -->
+    <string name="usb_midi_peripheral_manufacturer_name">Android</string>
+    <!-- Model name for USB MIDI Peripheral port -->
+    <string name="usb_midi_peripheral_model_name">USB Peripheral Port</string>
+
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 4329809..777924f 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -88,7 +88,7 @@
     </style>
 
     <style name="Preference.Material.DialogPreference.EditTextPreference">
-        <item name="dialogLayout">@layout/preference_dialog_edittext</item>
+        <item name="dialogLayout">@layout/preference_dialog_edittext_material</item>
     </style>
 
     <style name="Preference.Material.RingtonePreference">
@@ -464,7 +464,7 @@
 
     <!-- Colored bordered ink button -->
     <style name="Widget.Material.Button.Colored">
-        <item name="backgroundTint">@color/btn_material_accent</item>
+        <item name="backgroundTint">@color/btn_colored_material</item>
     </style>
 
     <!-- Small bordered ink button -->
@@ -481,8 +481,7 @@
 
     <!-- Colored borderless ink button -->
     <style name="Widget.Material.Button.Borderless.Colored">
-        <item name="textColor">?attr/colorAccent</item>
-        <item name="stateListAnimator">@anim/disabled_anim_material</item>
+        <item name="textColor">@color/btn_colored_text_material</item>
     </style>
 
     <!-- Alert dialog button bar button -->
@@ -770,6 +769,9 @@
         <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="popupBackground">@drawable/popup_background_material</item>
         <item name="popupElevation">@dimen/floating_window_z</item>
+        <item name="popupAnimationStyle">@empty</item>
+        <item name="popupEnterTransition">@transition/popup_window_enter</item>
+        <item name="popupExitTransition">@transition/popup_window_exit</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
         <item name="overlapAnchor">true</item>
@@ -780,11 +782,7 @@
     </style>
 
     <style name="Widget.Material.Spinner.DropDown"/>
-
-    <style name="Widget.Material.Spinner.DropDown.ActionBar">
-        <item name="background">@drawable/spinner_background_material</item>
-        <item name="overlapAnchor">true</item>
-    </style>
+    <style name="Widget.Material.Spinner.DropDown.ActionBar" />
 
     <style name="Widget.Material.Spinner.Underlined">
         <item name="background">@drawable/spinner_textfield_background_material</item>
@@ -847,7 +845,9 @@
         <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="popupBackground">@drawable/popup_background_material</item>
         <item name="popupElevation">@dimen/floating_window_z</item>
-        <item name="popupAnimationStyle">@style/Animation.Material.Popup</item>
+        <item name="popupAnimationStyle">@empty</item>
+        <item name="popupEnterTransition">@transition/popup_window_enter</item>
+        <item name="popupExitTransition">@transition/popup_window_exit</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
         <item name="dropDownWidth">wrap_content</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 574c099..fdaec80 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -327,6 +327,7 @@
   <java-symbol type="integer" name="config_wifi_framework_scan_result_rssi_level_patchup_value" />
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="string"  name="config_wifi_random_mac_oui" />
+  <java-symbol type="integer"  name="config_wifi_network_switching_blacklist_time" />
 
   <java-symbol type="bool" name="editable_voicemailnumber" />
 
@@ -1513,6 +1514,7 @@
   <java-symbol type="layout" name="screen_title" />
   <java-symbol type="layout" name="screen_title_icons" />
   <java-symbol type="string" name="system_ui_date_pattern" />
+  <java-symbol type="string" name="android_preparing_apk" />
   <java-symbol type="string" name="android_start_title" />
   <java-symbol type="string" name="android_upgrading_title" />
   <java-symbol type="string" name="bugreport_title" />
@@ -1529,6 +1531,7 @@
   <java-symbol type="string" name="global_action_silent_mode_on_status" />
   <java-symbol type="string" name="global_action_toggle_silent_mode" />
   <java-symbol type="string" name="global_action_lockdown" />
+  <java-symbol type="string" name="global_action_voice_assist" />
   <java-symbol type="string" name="invalidPuk" />
   <java-symbol type="string" name="lockscreen_carrier_default" />
   <java-symbol type="style" name="Animation.LockScreen" />
@@ -1616,6 +1619,7 @@
   <java-symbol type="drawable" name="ic_notification_ime_default" />
   <java-symbol type="drawable" name="ic_menu_refresh" />
   <java-symbol type="drawable" name="ic_settings" />
+  <java-symbol type="drawable" name="ic_voice_search" />
   <java-symbol type="drawable" name="stat_notify_car_mode" />
   <java-symbol type="drawable" name="stat_notify_disabled_data" />
   <java-symbol type="drawable" name="stat_notify_disk_full" />
@@ -2154,4 +2158,8 @@
 
   <java-symbol type="bool" name="config_use_sim_language_file" />
   <java-symbol type="bool" name="config_LTE_eri_for_network_name" />
+  <java-symbol type="bool" name="config_defaultInTouchMode" />
+
+  <java-symbol type="string" name="usb_midi_peripheral_manufacturer_name" />
+  <java-symbol type="string" name="usb_midi_peripheral_model_name" />
 </resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7804dd2..18c83b4 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -75,7 +75,7 @@
     <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
 
     <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
-    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}" />
+    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215" />
 
     <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
diff --git a/core/tests/coretests/res/layout/size_adaptive.xml b/core/tests/coretests/res/layout/size_adaptive.xml
deleted file mode 100644
index 03d0574..0000000
--- a/core/tests/coretests/res/layout/size_adaptive.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/multi1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_color.xml b/core/tests/coretests/res/layout/size_adaptive_color.xml
deleted file mode 100644
index cdb7a59..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_color.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:background="#ffffff"
-    android:id="@+id/multi1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml b/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml
deleted file mode 100644
index d24df5b..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:background="@drawable/size_adaptive_statelist"
-    android:id="@+id/multi1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u.xml b/core/tests/coretests/res/layout/size_adaptive_four_u.xml
deleted file mode 100644
index 232b921..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_four_u.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/gridLayout4"
-    android:layout_width="match_parent"
-    android:layout_height="256dp"
-    android:background="#000000"
-    android:columnCount="2"
-    android:padding="1dp" >
-
-    <ImageView
-        android:id="@+id/actor"
-        android:layout_width="62dp"
-        android:layout_height="62dp"
-        android:layout_row="0"
-        android:layout_column="0"
-        android:layout_rowSpan="2"
-        android:contentDescription="@string/actor"
-        android:src="@drawable/abe" />
-
-    <TextView
-        android:layout_width="0dp"
-        android:id="@+id/name"
-        android:layout_row="0"
-        android:layout_column="1"
-        android:layout_gravity="fill_horizontal"
-        android:padding="3dp"
-        android:text="@string/actor"
-        android:textColor="#ffffff"
-        android:textStyle="bold" />
-
-    <ImageView
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_row="1"
-        android:layout_column="1"
-        android:layout_gravity="fill_horizontal"
-        android:padding="5dp"
-        android:adjustViewBounds="true"
-        android:background="#555555"
-        android:scaleType="centerCrop"
-        android:src="@drawable/gettysburg"
-        android:contentDescription="@string/caption" />
-
-    <TextView
-        android:layout_width="0dp"
-        android:id="@+id/note"
-        android:layout_row="2"
-        android:layout_column="1"
-        android:layout_gravity="fill_horizontal"
-        android:padding="3dp"
-        android:singleLine="true"
-        android:text="@string/first"
-        android:textColor="#ffffff" />
-</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml
deleted file mode 100644
index 93a10de..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/gridLayout4"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="#000000"
-    android:columnCount="2"
-    android:padding="1dp"
-    android:orientation="horizontal" >
-
-    <ImageView
-        android:id="@+id/actor"
-        android:layout_width="62dp"
-        android:layout_height="62dp"
-        android:layout_row="0"
-        android:layout_column="0"
-        android:layout_rowSpan="2"
-        android:contentDescription="@string/actor"
-        android:src="@drawable/abe" />
-
-    <TextView
-        android:layout_width="0dp"
-        android:id="@+id/name"
-        android:layout_row="0"
-        android:layout_column="1"
-        android:layout_gravity="fill_horizontal"
-        android:padding="3dp"
-        android:text="@string/actor"
-        android:textColor="#ffffff"
-        android:textStyle="bold" />
-
-    <TextView
-        android:id="@+id/note"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_column="1"
-        android:layout_gravity="fill_horizontal"
-        android:layout_marginTop="5dp"
-        android:layout_row="1"
-        android:padding="3dp"
-        android:singleLine="false"
-        android:text="@string/first"
-        android:textColor="#ffffff" />
-
-    </GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_gappy.xml b/core/tests/coretests/res/layout/size_adaptive_gappy.xml
deleted file mode 100644
index d5e3b41..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_gappy.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/multi_with_gap"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="256dp"
-        internal:layout_minHeight="128dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_large_only.xml b/core/tests/coretests/res/layout/size_adaptive_large_only.xml
deleted file mode 100644
index cf58265..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_large_only.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/large_only_multi"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="256dp"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_lies.xml b/core/tests/coretests/res/layout/size_adaptive_lies.xml
deleted file mode 100644
index 7de892e..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_lies.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/multi1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u.xml b/core/tests/coretests/res/layout/size_adaptive_one_u.xml
deleted file mode 100644
index b6fe4a0..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_one_u.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/gridLayout1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp"
-    android:background="#000000"
-    android:columnCount="3"
-    android:padding="1dp"
-    android:rowCount="2" >
-
-    <ImageView
-        android:id="@+id/actor"
-        android:layout_width="62dp"
-        android:layout_height="62dp"
-        android:layout_column="0"
-        android:layout_row="0"
-        android:layout_rowSpan="2"
-        android:contentDescription="@string/actor"
-        android:src="@drawable/abe" />
-
-    <TextView
-        android:id="@+id/name"
-        android:layout_gravity="fill"
-        android:padding="3dp"
-        android:text="@string/actor"
-        android:textColor="#ffffff"
-        android:textStyle="bold" />
-
-    <ImageView
-        android:layout_width="62dp"
-        android:layout_height="62dp"
-        android:layout_gravity="fill_vertical"
-        android:layout_rowSpan="2"
-        android:adjustViewBounds="true"
-        android:background="#555555"
-        android:padding="2dp"
-        android:scaleType="fitXY"
-        android:src="@drawable/gettysburg"
-        android:contentDescription="@string/caption" />
-
-    <TextView
-        android:id="@+id/note"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_gravity="fill"
-        android:layout_marginTop="5dp"
-        android:padding="3dp"
-        android:singleLine="true"
-        android:text="@string/first"
-        android:textColor="#ffffff" />
-
-</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml
deleted file mode 100644
index df54eb6..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/gridLayout1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp"
-    android:background="#000000"
-    android:columnCount="2"
-    android:padding="1dp"
-    android:rowCount="2" >
-
-    <ImageView
-        android:id="@+id/actor"
-        android:layout_width="62dp"
-        android:layout_height="62dp"
-        android:layout_column="0"
-        android:layout_row="0"
-        android:layout_rowSpan="2"
-        android:contentDescription="@string/actor"
-        android:src="@drawable/abe" />
-
-    <TextView
-        android:id="@+id/name"
-        android:layout_gravity="fill"
-        android:padding="3dp"
-        android:text="@string/actor"
-        android:textColor="#ffffff"
-        android:textStyle="bold" />
-
-    <TextView
-        android:id="@+id/note"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_gravity="fill"
-        android:layout_marginTop="5dp"
-        android:padding="3dp"
-        android:singleLine="true"
-        android:text="@string/first"
-        android:textColor="#ffffff" />
-
-</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml b/core/tests/coretests/res/layout/size_adaptive_overlapping.xml
deleted file mode 100644
index 4abe8b0..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/multi_with_overlap"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="256dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="256dp"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_singleton.xml b/core/tests/coretests/res/layout/size_adaptive_singleton.xml
deleted file mode 100644
index eba387f..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_singleton.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u_text"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_text.xml b/core/tests/coretests/res/layout/size_adaptive_text.xml
deleted file mode 100644
index a9f0ba9..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_text.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/multi1"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u_text"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u_text"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_three_way.xml b/core/tests/coretests/res/layout/size_adaptive_three_way.xml
deleted file mode 100644
index 1eb5396..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_three_way.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/three_way_multi"
-    android:layout_width="match_parent"
-    android:layout_height="64dp" >
-
-    <include
-        android:id="@+id/one_u"
-        layout="@layout/size_adaptive_one_u"
-        android:layout_width="fill_parent"
-        android:layout_height="64dp"
-        internal:layout_minHeight="64dp"
-        internal:layout_maxHeight="64dp"
-        />
-
-    <include
-        android:id="@+id/two_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="128dp"
-        internal:layout_minHeight="65dp"
-        internal:layout_maxHeight="128dp"/>
-
-    <include
-        android:id="@+id/four_u"
-        layout="@layout/size_adaptive_four_u"
-        android:layout_width="fill_parent"
-        android:layout_height="256dp"
-        internal:layout_minHeight="129dp"
-        internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index e7f4bad..e5a92bf 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -11,7 +11,7 @@
 
     public void testSmallList() throws Exception {
         final int objectCount = 100;
-        List<SmallObject> list = new ArrayList<>();
+        List<SmallObject> list = new ArrayList<SmallObject>();
         for (int i = 0; i < objectCount; i++) {
             list.add(new SmallObject(i * 2, (i * 2) + 1));
         }
@@ -20,7 +20,7 @@
 
         Parcel parcel = Parcel.obtain();
         try {
-            parcel.writeParcelable(new ParceledListSlice<>(list), 0);
+            parcel.writeParcelable(new ParceledListSlice<SmallObject>(list), 0);
             parcel.setDataPosition(0);
             slice = parcel.readParcelable(getClass().getClassLoader());
         } finally {
@@ -56,7 +56,7 @@
         final int thresholdBytes = 256 * 1024;
         final int objectCount = thresholdBytes / measureLargeObject();
 
-        List<LargeObject> list = new ArrayList<>();
+        List<LargeObject> list = new ArrayList<LargeObject>();
         for (int i = 0; i < objectCount; i++) {
             list.add(new LargeObject(
                     i * 5,
@@ -71,7 +71,7 @@
 
         Parcel parcel = Parcel.obtain();
         try {
-            parcel.writeParcelable(new ParceledListSlice<>(list), 0);
+            parcel.writeParcelable(new ParceledListSlice<LargeObject>(list), 0);
             parcel.setDataPosition(0);
             slice = parcel.readParcelable(getClass().getClassLoader());
         } finally {
@@ -95,7 +95,7 @@
      * Test that only homogeneous elements may be unparceled.
      */
     public void testHomogeneousElements() throws Exception {
-        List<BaseObject> list = new ArrayList<>();
+        List<BaseObject> list = new ArrayList<BaseObject>();
         list.add(new LargeObject(0, 1, 2, 3, 4));
         list.add(new SmallObject(5, 6));
         list.add(new SmallObject(7, 8));
diff --git a/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java b/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java
new file mode 100644
index 0000000..59f780f
--- /dev/null
+++ b/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.RouteInfo;
+import android.net.StaticIpConfiguration;
+import android.os.Parcel;
+
+import java.net.InetAddress;
+import java.util.HashSet;
+
+import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.*;
+
+
+public class StaticIpConfigurationTest extends TestCase {
+
+    private static final String ADDRSTR = "192.0.2.2/25";
+    private static final LinkAddress ADDR = new LinkAddress(ADDRSTR);
+    private static final InetAddress GATEWAY = IpAddress("192.0.2.1");
+    private static final InetAddress OFFLINKGATEWAY = IpAddress("192.0.2.129");
+    private static final InetAddress DNS1 = IpAddress("8.8.8.8");
+    private static final InetAddress DNS2 = IpAddress("8.8.4.4");
+    private static final InetAddress DNS3 = IpAddress("4.2.2.2");
+    private static final String IFACE = "eth0";
+
+    private static InetAddress IpAddress(String addr) {
+        return InetAddress.parseNumericAddress(addr);
+    }
+
+    private void checkEmpty(StaticIpConfiguration s) {
+        assertNull(s.ipAddress);
+        assertNull(s.gateway);
+        assertNull(s.domains);
+        assertEquals(0, s.dnsServers.size());
+    }
+
+    private boolean isEqual(StaticIpConfiguration s1, StaticIpConfiguration s2) {
+        return s1.equals(s2);
+    }
+
+    private void assertEquals(StaticIpConfiguration s1, StaticIpConfiguration s2) {
+        assertTrue(isEqual(s1, s2));
+    }
+
+    private void assertNotEquals(StaticIpConfiguration s1, StaticIpConfiguration s2) {
+        assertFalse(isEqual(s1, s2));
+    }
+
+    private StaticIpConfiguration makeTestObject() {
+        StaticIpConfiguration s = new StaticIpConfiguration();
+        s.ipAddress = ADDR;
+        s.gateway = GATEWAY;
+        s.dnsServers.add(DNS1);
+        s.dnsServers.add(DNS2);
+        s.dnsServers.add(DNS3);
+        s.domains = "google.com";
+        return s;
+    }
+
+    @SmallTest
+    public void testConstructor() {
+        StaticIpConfiguration s = new StaticIpConfiguration();
+        checkEmpty(s);
+    }
+
+    @SmallTest
+    public void testCopyAndClear() {
+        StaticIpConfiguration empty = new StaticIpConfiguration((StaticIpConfiguration) null);
+        checkEmpty(empty);
+
+        StaticIpConfiguration s1 = makeTestObject();
+        StaticIpConfiguration s2 = new StaticIpConfiguration(s1);
+        assertEquals(s1, s2);
+        s2.clear();
+        assertEquals(empty, s2);
+    }
+
+    @SmallTest
+    public void testHashCodeAndEquals() {
+        HashSet<Integer> hashCodes = new HashSet();
+        hashCodes.add(0);
+
+        StaticIpConfiguration s = new StaticIpConfiguration();
+        // Check that this hash code is nonzero and different from all the ones seen so far.
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        s.ipAddress = ADDR;
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        s.gateway = GATEWAY;
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        s.dnsServers.add(DNS1);
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        s.dnsServers.add(DNS2);
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        s.dnsServers.add(DNS3);
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        s.domains = "example.com";
+        assertTrue(hashCodes.add(s.hashCode()));
+
+        assertFalse(s.equals(null));
+        assertEquals(s, s);
+
+        StaticIpConfiguration s2 = new StaticIpConfiguration(s);
+        assertEquals(s, s2);
+
+        s.ipAddress = new LinkAddress(DNS1, 32);
+        assertNotEquals(s, s2);
+
+        s2 = new StaticIpConfiguration(s);
+        s.domains = "foo";
+        assertNotEquals(s, s2);
+
+        s2 = new StaticIpConfiguration(s);
+        s.gateway = DNS2;
+        assertNotEquals(s, s2);
+
+        s2 = new StaticIpConfiguration(s);
+        s.dnsServers.add(DNS3);
+        assertNotEquals(s, s2);
+    }
+
+    @SmallTest
+    public void testToLinkProperties() {
+        LinkProperties expected = new LinkProperties();
+        expected.setInterfaceName(IFACE);
+
+        StaticIpConfiguration s = new StaticIpConfiguration();
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        final RouteInfo connectedRoute = new RouteInfo(new IpPrefix(ADDRSTR), null, IFACE);
+        s.ipAddress = ADDR;
+        expected.addLinkAddress(ADDR);
+        expected.addRoute(connectedRoute);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        s.gateway = GATEWAY;
+        RouteInfo defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), GATEWAY, IFACE);
+        expected.addRoute(defaultRoute);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        s.gateway = OFFLINKGATEWAY;
+        expected.removeRoute(defaultRoute);
+        defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), OFFLINKGATEWAY, IFACE);
+        expected.addRoute(defaultRoute);
+
+        RouteInfo gatewayRoute = new RouteInfo(new IpPrefix("192.0.2.129/32"), null, IFACE);
+        expected.addRoute(gatewayRoute);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        s.dnsServers.add(DNS1);
+        expected.addDnsServer(DNS1);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        s.dnsServers.add(DNS2);
+        s.dnsServers.add(DNS3);
+        expected.addDnsServer(DNS2);
+        expected.addDnsServer(DNS3);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        s.domains = "google.com";
+        expected.setDomains("google.com");
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        s.gateway = null;
+        expected.removeRoute(defaultRoute);
+        expected.removeRoute(gatewayRoute);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+
+        // Without knowing the IP address, we don't have a directly-connected route, so we can't
+        // tell if the gateway is off-link or not and we don't add a host route. This isn't a real
+        // configuration, but we should at least not crash.
+        s.gateway = OFFLINKGATEWAY;
+        s.ipAddress = null;
+        expected.removeLinkAddress(ADDR);
+        expected.removeRoute(connectedRoute);
+        expected.addRoute(defaultRoute);
+        assertEquals(expected, s.toLinkProperties(IFACE));
+    }
+
+    private StaticIpConfiguration passThroughParcel(StaticIpConfiguration s) {
+        Parcel p = Parcel.obtain();
+        StaticIpConfiguration s2 = null;
+        try {
+            s.writeToParcel(p, 0);
+            p.setDataPosition(0);
+            s2 = StaticIpConfiguration.CREATOR.createFromParcel(p);
+        } finally {
+            p.recycle();
+        }
+        assertNotNull(s2);
+        return s2;
+    }
+
+    @SmallTest
+    public void testParceling() {
+        StaticIpConfiguration s = makeTestObject();
+        StaticIpConfiguration s2 = passThroughParcel(s);
+        assertEquals(s, s2);
+    }
+}
+
diff --git a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java
deleted file mode 100644
index 18411b0..0000000
--- a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import com.android.frameworks.coretests.R;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.internal.widget.SizeAdaptiveLayout;
-
-
-public class SizeAdaptiveLayoutTest extends AndroidTestCase {
-
-    private LayoutInflater mInflater;
-    private int mOneU;
-    private int mFourU;
-    private SizeAdaptiveLayout mSizeAdaptiveLayout;
-    private View mSmallView;
-    private View mMediumView;
-    private View mLargeView;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        // inflate the layout
-        final Context context = getContext();
-        mInflater = LayoutInflater.from(context);
-        mOneU = 64;
-        mFourU = 4 * mOneU;
-    }
-
-    private void inflate(int resource){
-        mSizeAdaptiveLayout = (SizeAdaptiveLayout) mInflater.inflate(resource, null);
-        mSizeAdaptiveLayout.onAttachedToWindow();
-
-        mSmallView = mSizeAdaptiveLayout.findViewById(R.id.one_u);
-        mMediumView = mSizeAdaptiveLayout.findViewById(R.id.two_u);
-        mLargeView = mSizeAdaptiveLayout.findViewById(R.id.four_u);
-    }
-
-    /**
-     * The name 'test preconditions' is a convention to signal that if this
-     * test doesn't pass, the test case was not set up properly and it might
-     * explain any and all failures in other tests.  This is not guaranteed
-     * to run before other tests, as junit uses reflection to find the tests.
-     */
-    @SmallTest
-    public void testPreconditions() {
-        assertNotNull(mInflater);
-
-        inflate(R.layout.size_adaptive);
-        assertNotNull(mSizeAdaptiveLayout);
-        assertNotNull(mSmallView);
-        assertNotNull(mLargeView);
-    }
-
-    @SmallTest
-    public void testOpenLarge() {
-        inflate(R.layout.size_adaptive);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.minHeight + 10;
-
-        measureAndLayout(height);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-        assertEquals("1U should be gone",
-                View.GONE,
-                mSmallView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenSmall() {
-        inflate(R.layout.size_adaptive);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-        assertEquals("4U should be gone",
-                View.GONE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenTooSmall() {
-        inflate(R.layout.size_adaptive);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.minHeight - 10;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-        assertEquals("4U should be gone",
-                View.GONE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenTooBig() {
-        inflate(R.layout.size_adaptive);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        lp.maxHeight = 500;
-        mLargeView.setLayoutParams(lp);
-        int height = (int) (lp.minHeight + 10);
-
-        measureAndLayout(height);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-        assertEquals("1U should be gone",
-                View.GONE,
-                mSmallView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenWrapContent() {
-        inflate(R.layout.size_adaptive_text);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.minHeight + 10;
-
-        // manually measure it, and lay it out
-        int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
-        mSizeAdaptiveLayout.measure(500, measureSpec);
-        assertTrue("should not be forced to 4U",
-                mSizeAdaptiveLayout.getMeasuredHeight() < mFourU);
-    }
-
-    @SmallTest
-    public void testOpenOneUOnlySmall() {
-        inflate(R.layout.size_adaptive_singleton);
-        assertNull("largeView should be NULL in the singleton layout", mLargeView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.minHeight - 10;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenOneUOnlyLarge() {
-        inflate(R.layout.size_adaptive_singleton);
-        assertNull("largeView should be NULL in the singleton layout", mLargeView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.maxHeight + 10;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenOneUOnlyJustRight() {
-        inflate(R.layout.size_adaptive_singleton);
-        assertNull("largeView should be NULL in the singleton layout", mLargeView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenFourUOnlySmall() {
-        inflate(R.layout.size_adaptive_large_only);
-        assertNull("smallView should be NULL in the singleton layout", mSmallView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.minHeight - 10;
-
-        measureAndLayout(height);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenFourUOnlyLarge() {
-        inflate(R.layout.size_adaptive_large_only);
-        assertNull("smallView should be NULL in the singleton layout", mSmallView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.maxHeight + 10;
-
-        measureAndLayout(height);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenFourUOnlyJustRight() {
-        inflate(R.layout.size_adaptive_large_only);
-        assertNull("smallView should be NULL in the singleton layout", mSmallView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenIntoAGap() {
-        inflate(R.layout.size_adaptive_gappy);
-
-        SizeAdaptiveLayout.LayoutParams smallParams =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        SizeAdaptiveLayout.LayoutParams largeParams =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        assertTrue("gappy layout should have a gap",
-                smallParams.maxHeight + 10 < largeParams.minHeight);
-        int height = (int) smallParams.maxHeight + 10;
-
-        measureAndLayout(height);
-
-        assertTrue("one and only one view should be visible",
-                mLargeView.getVisibility() != mSmallView.getVisibility());
-        // behavior is undefined in this case.
-    }
-
-    @SmallTest
-    public void testOpenIntoAnOverlap() {
-        inflate(R.layout.size_adaptive_overlapping);
-
-        SizeAdaptiveLayout.LayoutParams smallParams =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        SizeAdaptiveLayout.LayoutParams largeParams =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        assertEquals("overlapping layout should overlap",
-                smallParams.minHeight,
-                largeParams.minHeight);
-        int height = (int) smallParams.maxHeight;
-
-        measureAndLayout(height);
-
-        assertTrue("one and only one view should be visible",
-                mLargeView.getVisibility() != mSmallView.getVisibility());
-        assertEquals("1U should get priority in an overlap because it is first",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenThreeWayViewSmall() {
-        inflate(R.layout.size_adaptive_three_way);
-        assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-        assertEquals("2U should be gone",
-                View.GONE,
-                mMediumView.getVisibility());
-        assertEquals("4U should be gone",
-                View.GONE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenThreeWayViewMedium() {
-        inflate(R.layout.size_adaptive_three_way);
-        assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mMediumView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be gone",
-                View.GONE,
-                mSmallView.getVisibility());
-        assertEquals("2U should be visible",
-                View.VISIBLE,
-                mMediumView.getVisibility());
-        assertEquals("4U should be gone",
-                View.GONE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testOpenThreeWayViewLarge() {
-        inflate(R.layout.size_adaptive_three_way);
-        assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
-
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be gone",
-                View.GONE,
-                mSmallView.getVisibility());
-        assertEquals("2U should be gone",
-                View.GONE,
-                mMediumView.getVisibility());
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-    }
-
-    @SmallTest
-    public void testResizeWithoutAnimation() {
-        inflate(R.layout.size_adaptive);
-
-        SizeAdaptiveLayout.LayoutParams largeParams =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int startHeight = (int) largeParams.minHeight + 10;
-        int endHeight = (int) largeParams.minHeight + 10;
-
-        measureAndLayout(startHeight);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-        assertFalse("There should be no animation on initial rendering.",
-                    mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
-
-        measureAndLayout(endHeight);
-
-        assertEquals("4U should still be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-        assertFalse("There should be no animation on scale within a view.",
-                    mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
-    }
-
-    @SmallTest
-    public void testResizeWithAnimation() {
-        inflate(R.layout.size_adaptive);
-
-        SizeAdaptiveLayout.LayoutParams smallParams =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        SizeAdaptiveLayout.LayoutParams largeParams =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int startHeight = (int) largeParams.minHeight + 10;
-        int endHeight = (int) smallParams.maxHeight;
-
-        measureAndLayout(startHeight);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-        assertFalse("There should be no animation on initial rendering.",
-                    mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
-
-        measureAndLayout(endHeight);
-
-        assertEquals("1U should now be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-        assertTrue("There should be an animation on scale between views.",
-                   mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
-    }
-
-    @SmallTest
-    public void testModestyPanelChangesColorWhite() {
-        inflate(R.layout.size_adaptive_color);
-        View panel = mSizeAdaptiveLayout.getModestyPanel();
-        assertTrue("ModestyPanel should have a ColorDrawable background",
-                   panel.getBackground() instanceof ColorDrawable);
-        ColorDrawable panelColor = (ColorDrawable) panel.getBackground();
-        ColorDrawable salColor = (ColorDrawable) mSizeAdaptiveLayout.getBackground();
-        assertEquals("ModestyPanel color should match the SizeAdaptiveLayout",
-                     panelColor.getColor(), salColor.getColor());
-    }
-
-    @SmallTest
-    public void testModestyPanelTracksStateListColor() {
-        inflate(R.layout.size_adaptive_color_statelist);
-        View panel = mSizeAdaptiveLayout.getModestyPanel();
-        assertEquals("ModestyPanel should have a ColorDrawable background" ,
-                     panel.getBackground().getClass(), ColorDrawable.class);
-        ColorDrawable panelColor = (ColorDrawable) panel.getBackground();
-        assertEquals("ModestyPanel color should match the SizeAdaptiveLayout",
-                     panelColor.getColor(), Color.RED);
-    }
-    @SmallTest
-    public void testOpenSmallEvenWhenLargeIsActuallySmall() {
-        inflate(R.layout.size_adaptive_lies);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("1U should be visible",
-                View.VISIBLE,
-                mSmallView.getVisibility());
-        assertTrue("1U should also have been measured",
-                   mSmallView.getMeasuredHeight() > 0);
-    }
-
-    @SmallTest
-    public void testOpenLargeEvenWhenLargeIsActuallySmall() {
-        inflate(R.layout.size_adaptive_lies);
-        SizeAdaptiveLayout.LayoutParams lp =
-          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
-        int height = (int) lp.minHeight;
-
-        measureAndLayout(height);
-
-        assertEquals("4U should be visible",
-                View.VISIBLE,
-                mLargeView.getVisibility());
-        assertTrue("4U should also have been measured",
-                   mLargeView.getMeasuredHeight() > 0);
-    }
-
-    private void measureAndLayout(int height) {
-        // manually measure it, and lay it out
-        int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
-        mSizeAdaptiveLayout.measure(500, measureSpec);
-        mSizeAdaptiveLayout.layout(0, 0, 500, mSizeAdaptiveLayout.getMeasuredHeight());
-    }
-}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index 0002ba7..7bf5f65 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -33,7 +33,8 @@
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
 LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
     -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-ifeq ($(LOCAL_USE_JACK),true)
+
+ifdef LOCAL_USE_JACK
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
 endif
 
@@ -41,10 +42,11 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 	echo "com/android/multidexlegacyandexception/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-
+endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index c9dbb57..416c238 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -33,7 +33,7 @@
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
 LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
     -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-ifeq ($(LOCAL_USE_JACK),true)
+ifdef LOCAL_USE_JACK
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
 endif
 
@@ -41,12 +41,14 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
+endif
 
 ## The application with a full main dex
 include $(CLEAR_VARS)
@@ -67,7 +69,7 @@
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2)
 LOCAL_JACK_FLAGS := -D jack.dex.output.policy=multidex -D jack.preprocessor=true\
     -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-ifeq ($(LOCAL_USE_JACK),true)
+ifdef LOCAL_USE_JACK
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
 endif
 
@@ -75,9 +77,11 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList2): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList2)
+endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index 16e396b..83ead4b 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -36,9 +36,10 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 
 $(built_dex_intermediate): $(mainDexList)
-
+endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index c62238b..d706ca9 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -32,7 +32,8 @@
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
 LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
     -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-ifeq ($(LOCAL_USE_JACK),true)
+
+ifdef LOCAL_USE_JACK
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
 endif
 
@@ -40,9 +41,11 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
+endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index 8c0c5d5..99b2a8b 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -32,7 +32,8 @@
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
 LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
     -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-ifeq ($(LOCAL_USE_JACK),true)
+
+ifdef LOCAL_USE_JACK
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
 endif
 
@@ -40,9 +41,11 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
+endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 002c1cc..3ee1c22 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -32,7 +32,8 @@
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
 LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
     -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-ifeq ($(LOCAL_USE_JACK),true)
+
+ifdef LOCAL_USE_JACK
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
 endif
 
@@ -40,10 +41,12 @@
 
 include $(BUILD_PACKAGE)
 
+ifndef LOCAL_USE_JACK
 $(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
 	$(hide) mkdir -p $(dir $@)
 	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
+endif
 
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 5265f20..4e03108 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -57,7 +57,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on January 5, 2015.
+<p style="clear:both"><em>Data collected during a 7-day period ending on February 2, 2015.
 <br/>Any versions with less than 0.1% distribution are not shown.</em>
 </p>
 
@@ -88,7 +88,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on January 5, 2015.
+<p style="clear:both"><em>Data collected during a 7-day period ending on February 2, 2015.
 
 <br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
 
@@ -108,7 +108,7 @@
 
 
 <img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A69.9%2C30.1&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A68.9%2C31.1&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
 
 
 <p>To declare which version of OpenGL ES your application requires, you should use the {@code
@@ -127,17 +127,17 @@
 </tr>
 <tr>
 <td>2.0</td>
-<td>69.9%</td>
+<td>68.9%</td>
 </tr>
 <tr>
 <td>3.0</td>
-<td>30.1%</td>
+<td>31.1%</td>
 </tr>
 </table>
 
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on January 5, 2015</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on February 2, 2015</em></p>
 
 
 
@@ -155,7 +155,7 @@
 var VERSION_DATA =
 [
   {
-    "chart": "//chart.googleapis.com/chart?cht=p&chs=500x250&chco=c4df9b%2C6fad0c&chd=t%3A0.4%2C7.8%2C6.7%2C46.0%2C39.1&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat",
+    "chart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A0.4%2C7.4%2C6.4%2C44.5%2C39.7%2C1.6&chco=c4df9b%2C6fad0c&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop&chs=500x250&cht=p",
     "data": [
       {
         "api": 8,
@@ -165,32 +165,37 @@
       {
         "api": 10,
         "name": "Gingerbread",
-        "perc": "7.8"
+        "perc": "7.4"
       },
       {
         "api": 15,
         "name": "Ice Cream Sandwich",
-        "perc": "6.7"
+        "perc": "6.4"
       },
       {
         "api": 16,
         "name": "Jelly Bean",
-        "perc": "19.2"
+        "perc": "18.4"
       },
       {
         "api": 17,
         "name": "Jelly Bean",
-        "perc": "20.3"
+        "perc": "19.8"
       },
       {
         "api": 18,
         "name": "Jelly Bean",
-        "perc": "6.5"
+        "perc": "6.3"
       },
       {
         "api": 19,
         "name": "KitKat",
-        "perc": "39.1"
+        "perc": "39.7"
+      },
+      {
+        "api": 21,
+        "name": "Lollipop",
+        "perc": "1.6"
       }
     ]
   }
@@ -203,29 +208,29 @@
     "data": {
       "Large": {
         "hdpi": "0.6",
-        "ldpi": "0.6",
-        "mdpi": "5.4",
-        "tvdpi": "2.3",
+        "ldpi": "0.5",
+        "mdpi": "5.1",
+        "tvdpi": "2.2",
         "xhdpi": "0.6"
       },
       "Normal": {
-        "hdpi": "37.5",
-        "mdpi": "8.8",
+        "hdpi": "38.3",
+        "mdpi": "8.7",
         "tvdpi": "0.1",
-        "xhdpi": "18.4",
-        "xxhdpi": "16.3"
+        "xhdpi": "18.8",
+        "xxhdpi": "15.9"
       },
       "Small": {
         "ldpi": "4.8"
       },
       "Xlarge": {
         "hdpi": "0.3",
-        "mdpi": "3.7",
+        "mdpi": "3.5",
         "xhdpi": "0.6"
       }
     },
-    "densitychart": "//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b%2C6fad0c&chd=t%3A5.4%2C17.9%2C2.4%2C38.4%2C19.6%2C16.3&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi",
-    "layoutchart": "//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b%2C6fad0c&chd=t%3A4.6%2C9.5%2C81.1%2C4.8&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall"
+    "densitychart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A5.3%2C17.3%2C2.3%2C39.2%2C20.0%2C15.9&chco=c4df9b%2C6fad0c&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chs=400x250&cht=p",
+    "layoutchart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A4.4%2C9.0%2C81.8%2C4.8&chco=c4df9b%2C6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall&chs=400x250&cht=p"
   }
 ];
 
@@ -305,7 +310,7 @@
   },
   {
     "api":21,
-    "link":"<a href='/about/versions/android-5.0.html'>4.4</a>",
+    "link":"<a href='/about/versions/android-5.0.html'>5.0</a>",
     "codename":"Lollipop"
   }
 ];
diff --git a/docs/html/google/play-services/ads.jd b/docs/html/google/play-services/ads.jd
index e4f0b2c..2f915f3 100644
--- a/docs/html/google/play-services/ads.jd
+++ b/docs/html/google/play-services/ads.jd
@@ -98,10 +98,8 @@
     serve banner and interstitial ads using the Google Mobile Ads APIs.</p>
 
     <h4>3. Read the documentation</h4>
-    <p>Read the <a class="external-link" href="https://www.google.com/adsense/localized-terms">AdSense
-    Terms of Service</a> and the <a class="external-link"
-    href="https://support.google.com/admob/topic/1307235?hl=en&ref_topic=1307209">AdMob
-    publisher guidelines and policies</a>.</p>
+    <p>Your use of the Google Mobile Ads SDK is governed by the terms between you and Google that
+    govern your use of the Google product (AdSense/AdMob, AdX or DFP) with which you use the SDK.</p>
     <p>For quick access while developing your Android apps, the <a
     href="{@docRoot}reference/gms-packages.html">Google Mobile Ads API reference</a> is available here on
     developer.android.com.</p>
diff --git a/docs/html/images/transitions/transition_sample_video.mp4 b/docs/html/images/transitions/transition_sample_video.mp4
new file mode 100644
index 0000000..37ae685
--- /dev/null
+++ b/docs/html/images/transitions/transition_sample_video.mp4
Binary files differ
diff --git a/docs/html/images/transitions/transition_sample_video.ogv b/docs/html/images/transitions/transition_sample_video.ogv
new file mode 100644
index 0000000..5598814
--- /dev/null
+++ b/docs/html/images/transitions/transition_sample_video.ogv
Binary files differ
diff --git a/docs/html/images/transitions/transition_sample_video.webm b/docs/html/images/transitions/transition_sample_video.webm
new file mode 100644
index 0000000..346ba8c
--- /dev/null
+++ b/docs/html/images/transitions/transition_sample_video.webm
Binary files differ
diff --git a/docs/html/images/transitions/transitions_diagram.png b/docs/html/images/transitions/transitions_diagram.png
new file mode 100644
index 0000000..9363940
--- /dev/null
+++ b/docs/html/images/transitions/transitions_diagram.png
Binary files differ
diff --git a/docs/html/images/tv/app-browse.png b/docs/html/images/tv/app-browse.png
new file mode 100644
index 0000000..7670713
--- /dev/null
+++ b/docs/html/images/tv/app-browse.png
Binary files differ
diff --git a/docs/html/images/tv/card-view.png b/docs/html/images/tv/card-view.png
new file mode 100644
index 0000000..5e907de
--- /dev/null
+++ b/docs/html/images/tv/card-view.png
Binary files differ
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index e74d46a..af6f6b8 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -481,14 +481,14 @@
 
 <ul>
 <li>GNOME or KDE desktop</li>
-<li>GNU C Library (glibc) 2.11 or later</li>
+<li>GNU C Library (glibc) 2.15 or later</li>
 <li>2 GB RAM minimum, 4 GB RAM recommended</li>
 <li>400 MB hard disk space</li>
 <li>At least 1 GB for Android SDK, emulator system images, and caches</li>
 <li>1280 x 800 minimum screen resolution</li>
 <li>Oracle&reg;  Java Development Kit (JDK) 7 </li>
 </ul>
-<p>Tested on Ubuntu&reg;  12.04, Precise Pangolin (64-bit distribution capable of running
+<p>Tested on Ubuntu&reg;  14.04, Trusty Tahr (64-bit distribution capable of running
 32-bit applications).</p>
 
 
diff --git a/docs/html/tools/building/plugin-for-gradle.jd b/docs/html/tools/building/plugin-for-gradle.jd
index 77cbfda..54a03fd 100644
--- a/docs/html/tools/building/plugin-for-gradle.jd
+++ b/docs/html/tools/building/plugin-for-gradle.jd
@@ -321,7 +321,7 @@
 machine and on other machines where Android Studio is not installed.</p>
 
 <p class="caution"><strong>Caution:</strong> When you create a project, only use the Gradle wrapper
-scripts and JAR from a trusted source, such as those generated by Android Studio. /p>
+scripts and JAR from a trusted source, such as those generated by Android Studio. </p>
 
 
 <h2 id="buildVariants"> Build variants</h2>
diff --git a/docs/html/tools/devices/emulator.jd b/docs/html/tools/devices/emulator.jd
index 42240b9..5bdd4e2 100644
--- a/docs/html/tools/devices/emulator.jd
+++ b/docs/html/tools/devices/emulator.jd
@@ -122,7 +122,7 @@
 mobile devices, including: </p>
 
 <ul>
-  <li>An ARMv5 CPU and the corresponding memory-management unit (MMU)</li>
+  <li>An ARMv5, ARMv7, or x86 CPU</li>
   <li>A 16-bit LCD display</li>
   <li>One or more keyboards (a Qwerty-based keyboard and associated Dpad/Phone
 buttons)</li>
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd
index 079dd71..0f0a0c0 100644
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/tools/support-library/features.jd
@@ -143,10 +143,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:support-v4:21.0.+
+com.android.support:support-v4:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
 
 
 <h2 id="multidex">Multidex Support Library</h2>
@@ -171,10 +170,9 @@
 </p>
 
 <pre>
-com.android.support:multidex:1.0.+
+com.android.support:multidex:1.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 1.0 prefix.</p>
 
 
 <h2 id="v7">v7 Support Libraries</h2>
@@ -226,10 +224,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:appcompat-v7:21.0.+
+com.android.support:appcompat-v7:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
 
 
 <h3 id="v7-cardview">v7 cardview library</h3>
@@ -249,10 +246,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:cardview-v7:21.0.+
+com.android.support:cardview-v7:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
 
 
 <h3 id="v7-gridlayout">v7 gridlayout library</h3>
@@ -271,10 +267,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:gridlayout-v7:21.0.+
+com.android.support:gridlayout-v7:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
 
 
 <h3 id="v7-mediarouter">v7 mediarouter library</h3>
@@ -308,7 +303,7 @@
 where "&lt;revision&gt;" is the minimum revision at which the library is available. For example:</p>
 
 <pre>
-com.android.support:mediarouter-v7:21.0.+
+com.android.support:mediarouter-v7:21.0.0
 </pre>
 
 <p class="caution">The v7 mediarouter library APIs introduced in Support Library
@@ -335,11 +330,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:palette-v7:21.0.+
+com.android.support:palette-v7:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
-
 
 
 <h3 id="v7-recyclerview">v7 recyclerview library</h3>
@@ -360,11 +353,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:recyclerview-v7:21.0.+
+com.android.support:recyclerview-v7:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
-
 
 
 <h2 id="v8">v8 Support Library</h2>
@@ -405,11 +396,9 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:support-v13:18.0.+
+com.android.support:support-v13:18.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 18.0 prefix.</p>
-
 
 
 <h2 id="v17-leanback">v17 Leanback Library</h2>
@@ -448,9 +437,8 @@
 <p>The Gradle build script dependency identifier for this library is as follows:</p>
 
 <pre>
-com.android.support:leanback-v17:21.0.+
+com.android.support:leanback-v17:21.0.0
 </pre>
 
-<p>This dependency notation specifies the latest release version with the 21.0 prefix.</p>
 
 
diff --git a/docs/html/training/basics/intents/sending.jd b/docs/html/training/basics/intents/sending.jd
index 4698ba1..b9463e4 100644
--- a/docs/html/training/basics/intents/sending.jd
+++ b/docs/html/training/basics/intents/sending.jd
@@ -153,7 +153,8 @@
 
 <pre>
 PackageManager packageManager = {@link android.content.Context#getPackageManager()};
-List&lt;ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
+List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,
+        PackageManager.MATCH_DEFAULT_ONLY);
 boolean isIntentSafe = activities.size() > 0;
 </pre>
 
diff --git a/docs/html/training/material/drawables.jd b/docs/html/training/material/drawables.jd
index 820a004..a2de8e9 100644
--- a/docs/html/training/material/drawables.jd
+++ b/docs/html/training/material/drawables.jd
@@ -73,7 +73,7 @@
 <pre>
 dependencies {
     ...
-    compile 'com.android.support:palette-v7:21.0.+'
+    compile 'com.android.support:palette-v7:21.0.0'
 }
 </pre>
 
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 0fcfb9c..c59d8ff 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -437,6 +437,35 @@
           </li>
         </ul>
       </li>
+
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot?>training/transitions/index.html"
+             description=
+             "How to animate state changes in a view hierarchy using transitions."
+            >Animating Views Using Scenes and Transitions</a>
+        </div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/transitions/overview.html">
+            The Transitions Framework
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/transitions/scenes.html">
+            Creating a Scene
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/transitions/transitions.html">
+            Applying a Transition
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/transitions/custom-transitions.html">
+            Creating Custom Transitions
+          </a>
+          </li>
+
+        </ul>
+      </li>
+
       <li class="nav-section">
         <div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html"
              description=
@@ -922,6 +951,10 @@
               Creating a Catalog Browser</a>
           </li>
           <li>
+            <a href="<?cs var:toroot ?>training/tv/playback/card.html">
+              Providing a Card View</a>
+          </li>
+          <li>
             <a href="<?cs var:toroot ?>training/tv/playback/details.html"
                ja-lang="詳細ビューをビルドする">
               Building a Details View</a>
@@ -1854,4 +1887,4 @@
     buildToggleLists();
     changeNavLang(getLangPref());
 //-->
-</script>
+</script>
\ No newline at end of file
diff --git a/docs/html/training/transitions/custom-transitions.jd b/docs/html/training/transitions/custom-transitions.jd
new file mode 100644
index 0000000..b64daaeb
--- /dev/null
+++ b/docs/html/training/transitions/custom-transitions.jd
@@ -0,0 +1,189 @@
+page.title=Creating Custom Transitions
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Extend">Extend the Transition Class</a></li>
+  <li><a href="#CaptureProperties">Capture View Property Values</a></li>
+  <li><a href="#CreateAnimator">Create a Custom Animator</a></li>
+  <li><a href="#Apply">Apply a Custom Transition</a></li>
+</ol>
+</div>
+</div>
+
+<p>A custom transition enables you to create an animation that is not available from any of
+the built-in transition classes. For example, you can define a custom transition that turns
+the foreground color of text and input fields to gray to indicate that the fields are disabled
+in the new screen. This type of change helps users see the fields you disabled.</p>
+
+<p>A custom transition, like one of the built-in transition types, applies animations to
+child views of both the starting and ending scenes. Unlike built-in transition types,
+however, you have to provide the code that captures property values and generates animations.
+You may also want to define a subset of target views for your animation.</p>
+
+<p>This lesson teaches you to capture property values and generate animations to create
+custom transitions.</p>
+
+
+
+<h2 id="Extend">Extend the Transition Class</h2>
+
+<p>To create a custom transition, add a class to your project that extends the {@link
+android.transition.Transition} class and override the methods shown in the following snippet:</p>
+
+<pre>
+public class CustomTransition extends Transition {
+
+    &#64;Override
+    public void captureStartValues(TransitionValues values) {}
+
+    &#64;Override
+    public void captureEndValues(TransitionValues values) {}
+
+    &#64;Override
+    public Animator createAnimator(ViewGroup sceneRoot,
+                                   TransitionValues startValues,
+                                   TransitionValues endValues) {}
+}
+</pre>
+
+<p>The following sections explain how to override these methods.</p>
+
+
+
+<h2 id="CaptureProperties">Capture View Property Values</h2>
+
+<p>Transition animations use the property animation system described in
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>. Property
+animations change a view property between a starting and ending value over a specified
+period of time, so the framework needs to have both the starting and ending value of
+the property to construct the animation.</p>
+
+<p>However, a property animation usually needs only a small subset of all the view's property
+values. For example, a color animation needs color property values, while a movement
+animation needs position property values. Since the property values needed for an animation
+are specific to a transition, the transitions framework does not provide every property value
+to a transition. Instead, the framework invokes callback methods that allow a transition to
+capture only the property values it needs and store them in the framework.</p>
+
+
+<h3 id="StartingValues">Capturing Starting Values</h3>
+
+<p>To pass the starting view values to the framework, implement the
+{@link android.transition.Transition#captureStartValues captureStartValues(transitionValues)}
+method. The framework calls this method for every view in the starting scene. The method
+argument is a {@link android.transition.TransitionValues} object that contains a reference
+to the view and a {@link java.util.Map} instance in which you can store the view values you
+want. In your implementation, retrieve these property values and pass them back to the
+framework by storing them in the map.</p>
+
+<p>To ensure that the key for a property value does not conflict with other {@link
+android.transition.TransitionValues} keys, use the following naming scheme:</p>
+
+<pre>
+package_name:transition_name:property_name
+</pre>
+
+<p>The following snippet shows an implementation of the {@link
+android.transition.Transition#captureStartValues captureStartValues()} method:</p>
+
+<pre>
+public class CustomTransition extends Transition {
+
+    // Define a key for storing a property value in
+    // TransitionValues.values with the syntax
+    // package_name:transition_class:property_name to avoid collisions
+    private static final String PROPNAME_BACKGROUND =
+            "com.example.android.customtransition:CustomTransition:background";
+
+    &#64;Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        // Call the convenience method captureValues
+        captureValues(transitionValues);
+    }
+
+
+    // For the view in transitionValues.view, get the values you
+    // want and put them in transitionValues.values
+    private void captureValues(TransitionValues transitionValues) {
+        // Get a reference to the view
+        View view = transitionValues.view;
+        // Store its background property in the values map
+        transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground());
+    }
+    ...
+}
+</pre>
+
+
+<h3 id="EndingValues">Capture Ending Values</h3>
+
+<p>The framework calls the {@link android.transition.Transition#captureEndValues} method
+once for every target view in the ending scene. In all other respects, {@link
+android.transition.Transition#captureEndValues captureEndValues()} works the same as {@link
+android.transition.Transition#captureStartValues captureStartValues()}.</p>
+
+<p>The following code snippet shows an implementation of the {@link
+android.transition.Transition#captureEndValues captureEndValues()} method:</p>
+
+<pre>
+&#64;Override
+public void captureEndValues(TransitionValues transitionValues) {
+    captureValues(transitionValues);
+}
+</pre>
+
+<p>In this example, both the {@link android.transition.Transition#captureStartValues
+captureStartValues()} and {@link android.transition.Transition#captureEndValues captureEndValues()}
+methods invoke <code>captureValues()</code> to retrieve and store values. The view property
+that <code>captureValues()</code> retrieves is the same, but it has different values in the
+starting and ending scenes. The framework maintains separate maps for the starting and ending
+states of a view.</p>
+
+
+
+<h2 id="CreateAnimator">Create a Custom Animator</h2>
+
+<p>To animate the changes to a view between its state in the starting scene and its state in
+the ending scene, you provide an animator by overriding the {@link
+android.transition.Transition#createAnimator createAnimator()} method. When the
+framework calls this method, it passes in the scene root view and the {@link
+android.transition.TransitionValues} objects that contain the starting and ending values
+you captured.</p>
+
+<p>The number of times the framework calls the {@link
+android.transition.Transition#createAnimator createAnimator()} method depends on the changes that
+occur between the starting and ending scenes. For example, consider a fade out/fade in animation
+implemented as a custom transition. If the starting scene has five targets of which two are
+removed from the ending scene, and the ending scene has the three targets from the starting
+scene plus a new target, then the framework calls {@link
+android.transition.Transition#createAnimator createAnimator()} six times: three of the calls
+animate the fading out and fading in of the targets that stay in both scene objects; two more calls
+animate the fading out of the targets removed from the ending scene; and one call
+animates the fading in of the new target in the ending scene.</p>
+
+<p>For target views that exist on both the starting and ending scenes, the framework provides
+a {@link android.transition.TransitionValues} object for both the <code>startValues</code> and
+<code>endValues</code> arguments. For target views that only exist in the starting or the
+ending scene, the framework provides a {@link android.transition.TransitionValues} object
+for the corresponding argument and <code>null</code> for the other.</p>
+
+<p>To implement the {@link android.transition.Transition#createAnimator} method when you create
+a custom transition, use the view property values you captured to create an {@link
+android.animation.Animator} object and return it to the framework. For an example implementation,
+see the <a
+href="{@docRoot}samples/CustomTransition/src/com.example.android.customtransition/ChangeColor.html">
+<code>ChangeColor</code></a> class in the <a href="{@docRoot}samples/CustomTransition/index.html">
+CustomTransition</a> sample. For more information about property animators, see
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>.</p>
+
+
+
+<h2 id="Apply">Apply a Custom Transition</h2>
+
+<p>Custom transitions work the same as built-in transitions. You can apply a custom transition
+using a transition manager, as described in <a
+href="{@docRoot}training/transitions/transitions.html#Apply">Applying a Transition</a>.</p>
diff --git a/docs/html/training/transitions/index.jd b/docs/html/training/transitions/index.jd
new file mode 100644
index 0000000..53faa01
--- /dev/null
+++ b/docs/html/training/transitions/index.jd
@@ -0,0 +1,80 @@
+page.title=Animating Views Using Scenes and Transitions
+
+@jd:body
+
+<!-- Sidebox -->
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>Dependencies and Prerequisites</h2>
+  <ul>
+    <li>Android 4.4.2 (API level 19) or higher</li>
+  </ul>
+  <h2>You should also read</h2>
+  <ul>
+    <li><a href="{@docRoot}guide/topics/ui/how-android-draws.html">
+        How Android Draws Views</a></li>
+  </ul>
+  <h2>Try it out</h2>
+  <ul>
+    <li><a href="{@docRoot}samples/BasicTransition/index.html">BasicTransition</a> sample</li>
+    <li><a href="{@docRoot}samples/CustomTransition/index.html">CustomTransition</a> sample</li>
+  </ul>
+</div>
+</div>
+
+<!-- Video box -->
+<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=S3H7nJ4QaD8">
+<div>
+  <h3>Video</h3>
+  <p>DevBytes: Android 4.4 Transitions</p>
+</div>
+</a>
+
+<p>The user interface of an activity often changes in response to user input and other events.
+For example, an activity that contains a form where users can type search queries can hide
+the form when the user submits it and show a list of search results in its place.</p>
+
+<p>To provide visual continuity in these situations, you can animate changes between
+different view hierarchies in your user interface. These animations give users feedback on
+their actions and help them learn how your app works.</p>
+
+<p>Android includes the <em>transitions framework</em>, which enables you to easily
+animate changes between two view hierarchies. The framework animates the views at runtime by
+changing some of their property values over time. The framework includes built-in animations
+for common effects and lets you create custom animations and transition lifecycle callbacks.</p>
+
+<p>This class teaches you to use the built-in animations in the transitions framework to
+animate changes between view hierarchies. This class also covers how to create custom
+animations.</p>
+
+<p class="note"><strong>Note:</strong> For Android versions earlier than 4.4.2 (API level 19)
+but greater than or equal to Android 4.0 (API level 14), use the <code>animateLayoutChanges</code>
+attribute to animate layouts. To learn more, see
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a> and
+<a href="{@docRoot}training/animation/layout.html">Animating Layout Changes</a>.</p>
+
+
+<h2>Lessons</h2>
+
+<dl>
+<dt><a href="{@docRoot}training/transitions/overview.html">
+The Transitions Framework</a></dt>
+<dd>
+  Learn the main features and components of the transitions framework.
+</dd>
+<dt><a href="{@docRoot}training/transitions/scenes.html">
+Creating a Scene</a></dt>
+<dd>
+  Learn how to create a scene to store the state of a view hierarchy.
+</dd>
+<dt><a href="{@docRoot}training/transitions/transitions.html">
+Applying a Transition</a></dt>
+<dd>
+  Learn how to apply a transition between two scenes of a view hierarchy.
+</dd>
+<dt><a href="{@docRoot}training/transitions/custom-transitions.html">
+Creating Custom Transitions</a></dt>
+<dd>
+  Learn how to create other animation effects not included in the transitions framework.
+</dd>
+</dl>
diff --git a/docs/html/training/transitions/overview.jd b/docs/html/training/transitions/overview.jd
new file mode 100644
index 0000000..044cf16
--- /dev/null
+++ b/docs/html/training/transitions/overview.jd
@@ -0,0 +1,165 @@
+page.title=The Transitions Framework
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson covers</h2>
+<ol>
+  <li><a href="#Overview">Overview</a></li>
+  <li><a href="#Scenes">Scenes</a></li>
+  <li><a href="#Transitions">Transitions</a></li>
+  <li><a href="#Limitations">Limitations</a></li>
+</ol>
+</div>
+</div>
+
+<p>Animating your app's user interface provides more than just visual appeal. Animations
+highlight changes and provide visual cues that help users learn how your app works.</p>
+
+<p>To help you animate a change between one view hierarchy and another, Android provides the
+transitions framework. This framework applies one or more animations to all the views in the
+hierarchies as it changes between them.</p>
+
+<p>The framework has the following features:</p>
+
+<dl>
+<dt><em>Group-level animations</em></dt>
+<dd>Applies one or more animation effects to all of the views in a view hierarchy.</dd>
+<dt><em>Transition-based animation</em></dt>
+<dd>Runs animations based on the changes between starting and ending view property values.</dd>
+<dt><em>Built-in animations</em></dt>
+<dd>Includes predefined animations for common effects such as fade out or movement.</dd>
+
+<!-- Figure 1 - Transitions video -->
+<div style="float:right;margin-left:30px;margin-top:10px">
+<div class="framed-nexus5-port-span-5" style="clear:left;">
+<video class="play-on-hover" height="442" autoplay="" poster="">
+<source src="{@docRoot}images/transitions/transition_sample_video.mp4" type="video/mp4">
+<source src="{@docRoot}images/transitions/transition_sample_video.ogv" type="video/ogg">
+<source src="{@docRoot}images/transitions/transition_sample_video.webm" type="video/webm">
+</video>
+</div>
+<p class="img-caption" style="margin-top:7px;margin-bottom:0px">
+<strong>Figure 1.</strong> Visual cues using user interface animation.</p>
+<div style="margin-top:5px;margin-bottom:20px;font-size:10pt" class="video-instructions">&nbsp;</div>
+</div>
+
+<dt><em>Resource file support</em></dt>
+<dd>Loads view hierarchies and built-in animations from layout resource files.</dd>
+<dt><em>Lifecycle callbacks</em></dt>
+<dd>Defines callbacks that provide finer control over the animation and hierarchy change
+process.</dd>
+</dl>
+
+
+
+<h2 id="Overview">Overview</h2>
+
+<p>The example in Figure 1 shows how an animation provides visual cues to help the user. As the
+app changes from its search entry screen to its search results screen, it fades out views that
+are no longer in use and fades in new views.</p>
+
+<p>This animation is an example of using the transitions framework. The framework
+animates changes to all the views in two view hierarchies. A view hierarchy can be as simple
+as a single view or as complex as a {@link android.view.ViewGroup} containing an elaborate
+tree of views. The framework animates each view by changing one or more of its property values
+over time between the initial or <em>starting</em> view hierarchy and the final or <em>ending</em>
+view hierarchy.</p>
+
+<p>The transitions framework works in parallel with view hierarchies and animations. The
+purpose of the framework is to store the state of view hierarchies, change between these
+hierarchies in order to modify the appearance of the device screen, and animate the change by
+storing and applying animation definitions.</p>
+
+<p>The diagram in Figure 2 illustrates the relationship between view hierarchies, framework
+objects, and animations:</p>
+
+<!-- Figure 2 - diagram -->
+<img src="{@docRoot}images/transitions/transitions_diagram.png"
+     width="506" height="234" alt="" style="margin-top:7px" />
+<p class="img-caption"><strong>Figure 2.</strong> Relationships in the transitions framework.</p>
+
+<p>The transitions framework provides abstractions for scenes, transitions, and transition
+managers. These are described in detail in the following sections. To use the framework, you
+create scenes for the view hierarchies in your app that you plan to change between. Next, you
+create a transition for each animation you want to use. To start the animation between two
+view hierarchies, you use a transition manager specifying the transition to use and the ending
+scene. This procedure is described in detail in the remaining lessons in this class.</p>
+
+
+
+<h2 id="Scenes">Scenes</h2>
+
+<p>A scene stores the state of a view hierarchy, including all its views and their property
+values. A view hierarchy can be a simple view or a complex tree of views and child layouts.
+Storing the view hierarchy state in a scene enables you to transition into that state from
+another scene. The framework provides the {@link android.transition.Scene} class to represent
+a scene.</p>
+
+<p>The transitions framework lets you create scenes from layout resource files or from
+{@link android.view.ViewGroup} objects in your code. Creating a scene in your code is useful
+if you generated a view hierarchy dynamically or if you are modifying it at runtime.</p>
+
+<p>In most cases, you do not create a starting scene explicitly. If you have applied a
+transition, the framework uses the previous ending scene as the starting scene for any
+subsequent transitions. If you have not applied a transition, the framework collects information
+about the views from the current state of the screen.</p>
+
+<p>A scene can also define its own actions that run when you make a scene change. For example,
+this feature is useful for cleaning up view settings after you transition to a scene.</p>
+
+<p>In addition to the view hierarchy and its property values, a scene also stores a reference
+to the parent of the view hierarchy. This root view is called a <strong>scene root</strong>.
+Changes to the scene and animations that affect the scene occur within the scene root.</p>
+
+<p>To learn how to create scenes, see
+<a href="{@docRoot}training/transitions/scenes.html">Creating a Scene</a>.</p>
+
+
+
+<h2 id="Transitions">Transitions</h2>
+
+<p>In the transitions framework, animations create a series of frames that depict a change
+between the view hierarchies in the starting and ending scenes. Information about the animation
+is stored in a {@link android.transition.Transition} object. To run the animation, you apply the
+transition using a {@link android.transition.TransitionManager} instance. The framework can
+transition between two different scenes or transition to a different state for the current
+scene.</p>
+
+<p>The framework includes a set of built-in transitions for commonly-used animation effects,
+such as fading and resizing views. You can also define your own custom transitions to create
+an animation effect using the APIs in the animations framework. The transitions framework also
+enables you to combine different animation effects in a transition set that contains a group
+of individual built-in or custom transitions.</p>
+
+<p>The transition lifecycle is similar to the activity lifecycle, and it represents the
+transition states that the framework monitors between the start and the completion of an
+animation. At important lifecycle states, the framework invokes callback methods that you can
+implement to make adjustments to your user interface at different phases of the transition.</p>
+
+<p>To learn more about transitions, see
+<a href="{@docRoot}training/transitions/transitions.html">Applying a Transition</a> and
+<a href="{@docRoot}training/transitions/custom-transitions.html">Creating Custom
+Transitions</a>.</p>
+
+
+
+<h2 id="Limitations">Limitations</h2>
+
+<p>This section lists some known limitations of the transitions framework:</p>
+
+<ul>
+<li>Animations applied to a {@link android.view.SurfaceView} may not appear correctly.
+{@link android.view.SurfaceView} instances are updated from a non-UI thread, so the updates
+may be out of sync with the animations of other views.</li>
+<li>Some specific transition types may not produce the desired animation effect when applied
+to a {@link android.view.TextureView}.</li>
+<li>Classes that extend {@link android.widget.AdapterView}, such as
+{@link android.widget.ListView}, manage their child views in ways that are incompatible with
+the transitions framework. If you try to animate a view based on
+{@link android.widget.AdapterView}, the device display may hang.</li>
+<li>If you try to resize a {@link android.widget.TextView} with an animation, the text will
+pop to a new location before the object has completely resized. To avoid this problem, do not
+animate the resizing of views that contain text.</li>
+</ul>
diff --git a/docs/html/training/transitions/scenes.jd b/docs/html/training/transitions/scenes.jd
new file mode 100644
index 0000000..4bf7d0e
--- /dev/null
+++ b/docs/html/training/transitions/scenes.jd
@@ -0,0 +1,211 @@
+page.title=Creating a Scene
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#FromLayout">Create a Scene From a Layout Resource</a></li>
+  <li><a href="#FromCode">Create a Scene in Your Code</a></li>
+  <li><a href="#Actions">Create Scene Actions</a></li>
+</ol>
+</div>
+</div>
+
+<p>Scenes store the state of a view hierarchy, including all its views and their property
+values. The transitions framework can run animations between a starting and an ending scene.
+The starting scene is often determined automatically from the current state of the user
+interface. For the ending scene, the framework enables you to create a scene from a layout
+resource file or from a group of views in your code.</p>
+
+<p>This lesson shows you how to create scenes in your app and how to define scene actions.
+The next lesson shows you how to transition between two scenes.</p>
+
+<p class="note"><strong>Note:</strong> The framework can animate changes in a single view
+hierarchy without using scenes, as described in
+<a href="{@docRoot}training/transitions/transitions.html#NoScenes">Apply a Transition Without
+Scenes</a>. However, understanding this lesson is essential to work with transitions.</p>
+
+
+
+<h2 id="FromLayout">Create a Scene From a Layout Resource</h2>
+
+<p>You can create a {@link android.transition.Scene} instance directly from a layout resource
+file. Use this technique when the view hierarchy in the file is mostly static. The resulting
+scene represents the state of the view hierarchy at the time you created the
+{@link android.transition.Scene} instance. If you change the view hierarchy, you have to
+recreate the scene. The framework creates the scene from the entire view hierarchy in the
+file; you can not create a scene from part of a layout file.</p>
+
+<p>To create a {@link android.transition.Scene} instance from a layout resource file, retrieve
+the scene root from your layout as a {@link android.view.ViewGroup} instance and then call the
+{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method with the
+scene root and the resource ID of the layout file that contains the view hierarchy for the
+scene.</p>
+
+<h3>Define Layouts for Scenes</h3>
+
+<p>The code snippets in the rest of this section show you how to create two different scenes
+with the same scene root element. The snippets also demonstrate that you can load multiple
+unrelated {@link android.transition.Scene} objects without implying that they are related to
+each other.</p>
+
+<p>The example consists of the following layout definitions:</p>
+
+<ul>
+<li>The main layout of an activity with a text label and a child layout.</li>
+<li>A relative layout for the first scene with two text fields.</li>
+<li>A relative layout for the second scene with the same two text fields in different order.</li>
+</ul>
+
+<p>The example is designed so that all of the animation occurs within the child layout of the
+main layout for the activity. The text label in the main layout remains static.</p>
+
+<p>The main layout for the activity is defined as follows:</p>
+
+<p class="code-caption">res/layout/activity_main.xml</p>
+
+<pre>
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/master_layout">
+    &lt;TextView
+        android:id="@+id/title"
+        ...
+        android:text="Title"/>
+    &lt;FrameLayout
+        android:id="@+id/scene_root">
+        &lt;include layout="@layout/a_scene" />
+    &lt;/FrameLayout>
+&lt;/LinearLayout>
+</pre>
+
+<p>This layout definition contains a text field and a child layout for the scene root. The
+layout for the first scene is included in the main layout file. This allows the app to display
+it as part of the initial user interface and also to load it into a scene, since the framework
+can load only a whole layout file into a scene.</p>
+
+<p>The layout for the first scene is defined as follows:</p>
+
+<p class="code-caption">res/layout/a_scene.xml</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scene_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    &lt;TextView
+        android:id="@+id/text_view1
+        android:text="Text Line 1" />
+    &lt;TextView
+        android:id="@+id/text_view2
+        android:text="Text Line 2" />
+&lt;/RelativeLayout>
+</pre>
+
+<p>The layout for the second scene contains the same two text fields (with the same IDs)
+placed in a different order and is defined as follows:</p>
+
+<p class="code-caption">res/layout/another_scene.xml</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scene_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    &lt;TextView
+        android:id="@+id/text_view2
+        android:text="Text Line 2" />
+    &lt;TextView
+        android:id="@+id/text_view1
+        android:text="Text Line 1" />
+&lt;/RelativeLayout>
+</pre>
+
+<h3>Generate Scenes from Layouts</h3>
+
+<p>After you create definitions for the two relative layouts, you can obtain an scene for
+each of them. This enables you to later transition between the two UI configurations.
+To obtain a scene, you need a reference to the scene root and the layout resource ID.</p>
+
+<p>The following code snippet shows you how to get a reference to the scene root and create
+two {@link android.transition.Scene} objects from the layout files:</p>
+
+<pre>
+Scene mAScene;
+Scene mAnotherScene;
+
+// Create the scene root for the scenes in this app
+mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);
+
+// Create the scenes
+mAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);
+mAnotherScene =
+    Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);
+</pre>
+
+<p>In the app, there are now two {@link android.transition.Scene} objects based on view
+hierarchies. Both scenes use the scene root defined by the
+{@link android.widget.FrameLayout} element in <code>res/layout/activity_main.xml</code>.</p>
+
+
+
+<h2 id="FromCode">Create a Scene in Your Code</h2>
+
+<p>You can also create a {@link android.transition.Scene} instance in your code from a
+{@link android.view.ViewGroup} object. Use this technique when you modify the view hierarchies
+directly in your code or when you generate them dynamically.</p>
+
+<p>To create a scene from a view hierarchy in your code, use the
+{@link android.transition.Scene#Scene(android.view.ViewGroup, android.view.View) Scene(sceneRoot, viewHierarchy)}
+constructor. Calling this constructor is equivalent to calling the
+{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method when you
+have already inflated a layout file.</p>
+
+<p>The following code snippet demonstrates how to create a {@link android.transition.Scene}
+instance from the scene root element and the view hierarchy for the scene in your code:</p>
+
+<pre>
+Scene mScene;
+
+// Obtain the scene root element
+mSceneRoot = (ViewGroup) mSomeLayoutElement;
+
+// Obtain the view hierarchy to add as a child of
+// the scene root when this scene is entered
+mViewHierarchy = (ViewGroup) someOtherLayoutElement;
+
+// Create a scene
+mScene = new Scene(mSceneRoot, mViewHierarchy);
+</pre>
+
+
+
+<h2 id="Actions">Create Scene Actions</h2>
+
+<p>The framework enables you to define custom scene actions that the system runs when entering
+or exiting a scene. In many cases, defining custom scene actions is not necessary, since the
+framework animates the change between scenes automatically.</p>
+
+<p>Scene actions are useful for handling these cases:</p>
+
+<ul>
+<li>Animate views that are not in the same hierarchy. You can animate views for both the
+starting and ending scenes using exit and entry scene actions.</li>
+<li>Animate views that the transitions framework cannot animate automatically, such as
+{@link android.widget.ListView} objects. For more information, see
+<a href="{@docRoot}training/transitions/overview.html#Limitations">Limitations</a>.</li>
+</ul>
+
+<p>To provide custom scene actions, define your actions as {@link java.lang.Runnable} objects
+and pass them to the {@link android.transition.Scene#setExitAction Scene.setExitAction()} or
+{@link android.transition.Scene#setEnterAction Scene.setEnterAction()} methods. The framework
+calls the {@link android.transition.Scene#setExitAction setExitAction()} method on the starting
+scene before running the transition animation and the {@link
+android.transition.Scene#setEnterAction setEnterAction()} method on the ending scene after
+running the transition animation.</p>
+
+<p class="note"><strong>Note:</strong> Do not use scene actions to pass data between views in
+the starting and ending scenes. For more information, see
+<a href="{@docRoot}training/transitions/transitions.html#Callbacks">Defining Transition
+Lifecycle Callbacks</a>.</p>
diff --git a/docs/html/training/transitions/transitions.jd b/docs/html/training/transitions/transitions.jd
new file mode 100644
index 0000000..489e291
--- /dev/null
+++ b/docs/html/training/transitions/transitions.jd
@@ -0,0 +1,315 @@
+page.title=Applying a Transition
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Create">Create a Transition</a></li>
+  <li><a href="#Apply">Apply a Transition</a></li>
+  <li><a href="#Targets">Choose Specific Target Views</a></li>
+  <li><a href="#Multiple">Specify Multiple Transitions</a></li>
+  <li><a href="#NoScenes">Apply a Transition Without Scenes</a></li>
+  <li><a href="#Callbacks">Define Transition Lifecycle Callbacks</a></li>
+</ol>
+</div>
+</div>
+
+<p>In the transitions framework, animations create a series of frames that depict a change
+between the view hierarchies in the starting and ending scenes. The framework represents
+these animations as transition objects, which contain information about an animation. To
+run an animation, you provide the transition to use and the ending scene to a transition
+manager.</p>
+
+<p>This lesson teaches you run an animation between two scenes using built-in transitions
+to move, resize, and fade views. The next lesson shows you how to define custom transitions.</p>
+
+
+
+<h2 id="Create">Create a Transition</h2>
+
+<p>In the previous lesson, you learned how to create scenes that represent the state of
+different view hierarchies. Once you have defined the starting scene and the ending scene you
+want to change between, you need to create a {@link android.transition.Transition} object
+that defines an animation. The framework enables you to specify a built-in transition in a
+resource file and inflate it in your code or to create an instance of a built-in transition
+directly in your code.</p>
+
+<!-- Built in transition table -->
+<p class="table-caption" id="table1"><strong>Table 1.</strong> Built-in transition types.</p>
+<table>
+<tr>
+  <th scope="col">Class</th>
+  <th scope="col">Tag</th>
+  <th scope="col">Attributes</th>
+  <th scope="col">Effect</th>
+</tr>
+<tr>
+  <td><code><a href="/reference/android/transition/AutoTransition.html">AutoTransition</a></code></td>
+  <td>&lt;autoTransition/&gt;</td>
+  <td style="text-align=center;"> - </td>
+  <td>Default transition. Fade out, move and resize, and fade in views, in that order.</td>
+</tr>
+<tr>
+  <td><code><a href="/reference/android/transition/Fade.html">Fade</a></code></td>
+  <td>&lt;fade/&gt;</td>
+  <td><code>android:fadingMode="[fade_in |<br> fade_out |<br> fade_in_out]"</code></td>
+  <td>
+    <code>fade_in</code> fades in views<br>
+    <code>fade_out</code> fades out views<br>
+    <code>fade_in_out</code> (default) does a <code>fade_out</code> followed by a <code>fade_in</code>.
+  </td>
+</tr>
+<tr>
+  <td><code><a href="/reference/android/transition/ChangeBounds.html">ChangeBounds</a></code></td>
+  <td>&lt;changeBounds/&gt;</td>
+  <td style="text-align=center;"> - </td>
+  <td>Moves and resizes views.</td>
+</tr>
+</table>
+
+
+<h3 id="FromFile">Create a transition instance from a resource file</h3>
+
+<p>This technique enables you to modify your transition definition without having to change
+the code of your activity. This technique is also useful to separate complex transition
+definitions from your application code, as shown in <a href="#Multiple">Specify Multiple
+Transitions</a>.</p>
+
+<p>To specify a built-in transition in a resource file, follow these steps:</p>
+
+<ol>
+<li>Add the <code>res/transition/</code> directory to your project.</li>
+<li>Create a new XML resource file inside this directory.</li>
+<li>Add an XML node for one of the built-in transitions.</li>
+</ol>
+
+<p>For example, the following resource file specifies the {@link android.transition.Fade}
+transition:</p>
+
+<p class="code-caption">res/transition/fade_transition.xml</p>
+
+<pre>
+&lt;fade xmlns:android="http://schemas.android.com/apk/res/android" />
+</pre>
+
+<p>The following code snippet shows how to inflate a {@link android.transition.Transition}
+instance inside your activity from a resource file:</p>
+
+<pre>
+Transition mFadeTransition =
+        TransitionInflater.from(this).
+        inflateTransition(R.transition.fade_transition);
+</pre>
+
+
+<h3 id="FromCode">Create a transition instance in your code</h3>
+
+<p>This technique is useful for creating transition objects dynamically if you modify the user
+interface in your code, and to create simple built-in transition instances with few or
+no parameters.</p>
+
+<p>To create an instance of a built-in transition, invoke one of the public constructors in
+the subclasses of the {@link android.transition.Transition} class. For example, the following
+code snippet creates an instance of the {@link android.transition.Fade} transition:</p>
+
+<pre>
+Transition mFadeTransition = new Fade();
+</pre>
+
+
+
+<h2 id="Apply">Apply a Transition</h2>
+
+<p>You typically apply a transition to change between different view hierarchies in response
+to an event, such as a user action. For example, consider a search app: when the user enters
+a search term and clicks the search button, the app changes to the scene that represents the
+results layout while applying a transition that fades out the search button and fades in the
+search results.</p>
+
+<p>To make a scene change while applying a transition in response to some event in your
+activity, call the {@link android.transition.TransitionManager#go TransitionManager.go()}
+static method with the ending scene and the transition instance to use for the animation,
+as shown in the following snippet:</p>
+
+<pre>
+TransitionManager.go(mEndingScene, mFadeTransition);
+</pre>
+
+<p>The framework changes the view hierarchy inside the scene root with the view hierarchy
+from the ending scene while running the animation specified by the transition instance. The
+starting scene is the ending scene from the last transition. If there was no previous
+transition, the starting scene is determined automatically from the current state of the
+user interface.</p>
+
+<p>If you do not specify a transition instance, the transition manager can apply an automatic
+transition that does something reasonable for most situations. For more information, see the
+API reference for the {@link android.transition.TransitionManager} class.</p>
+
+
+
+<h2 id="Targets">Choose Specific Target Views</h2>
+
+<p>The framework applies transitions to all views in the starting and ending scenes by
+default. In some cases, you may only want  to apply an animation to a subset of views in a
+scene. For example, the framework does not support animating changes to
+{@link android.widget.ListView} objects, so you should not try to animate them during a
+transition. The framework enables you to select specific views you want to animate.</p>
+
+<p>Each view that the transition animates is called a <em>target</em>. You can only
+select targets that are part of the view hierarchy associated with a scene.</p>
+
+<p>To remove one or more views from the list of targets, call the {@link
+android.transition.Transition#removeTarget removeTarget()} method before starting
+the transition. To add only the views you specify to the list of targets, call the
+{@link android.transition.Transition#addTarget addTarget()} method. For more
+information, see the API reference for the {@link android.transition.Transition} class.</p>
+
+
+
+<h2 id="Multiple">Specify Multiple Transitions</h2>
+
+<p>To get the most impact from an animation, you should match it to the type of changes
+that occur between the scenes. For example, if you are removing some views and adding others
+between scenes, a fade out/fade in animation provides a noticeable indication that some views
+are no longer available. If you are moving views to different points on the screen, a better
+choice would be to animate the movement so that users notice the new location of the views.</p>
+
+<p>You do not have to choose only one animation, since the transitions framework enables you
+to combine animation effects in a transition set that contains a group of individual built-in
+or custom transitions.</p>
+
+<p>To define a transition set from a collection of transitions in XML, create a resource file
+in the <code>res/transitions/</code> directory and list the transitions under the
+<code>transitionSet</code> element. For example, the following snippet shows how to specify a
+transition set that has the same behaviour as the {@link android.transition.AutoTransition}
+class:</p>
+
+<pre>
+&lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+    android:transitionOrdering="sequential">
+    &lt;fade android:fadingMode="fade_out" />
+    &lt;changeBounds />
+    &lt;fade android:fadingMode="fade_in" />
+&lt;/transitionSet>
+</pre>
+
+<p>To inflate the transition set into a {@link android.transition.TransitionSet} object in
+your code, call the {@link android.transition.TransitionInflater#from TransitionInflater.from()}
+method in your activity. The {@link android.transition.TransitionSet} class extends from the
+{@link android.transition.Transition} class, so you can use it with a transition manager just
+like any other {@link android.transition.Transition} instance.</p>
+
+
+
+<h2 id="NoScenes">Apply a Transition Without Scenes</h2>
+
+<p>Changing view hierarchies is not the only way to modify your user interface. You can also
+make changes by adding, modifying, and removing child views within the current hierarchy. For
+example, you can implement a search interaction with just a single layout. Start with the
+layout showing a search entry field and a search icon. To change the user interface to show
+the results, remove the search button when the user clicks it by calling the {@link
+android.view.ViewGroup#removeView ViewGroup.removeView()} method, and add the search results by
+calling {@link android.view.ViewGroup#addView ViewGroup.addView()} method.</p>
+
+<p>You may want to use this approach if the alternative is to have two hierarchies that are
+nearly identical. Rather than having to create and maintain two separate layout files for a
+minor difference in the user interface, you can have one layout file containing a view
+hierarchy that you modify in code.</p>
+
+<p>If you make changes within the current view hierarchy in this fashion, you do not need to
+create a scene. Instead, you can create and apply a transition between two states of a view
+hierarchy using a <em>delayed transition</em>. This feature of the transitions framework
+starts with the current view hierarchy state, records changes you make to its views, and applies
+a transition that animates the changes when the system redraws the user interface.</p>
+
+<p>To create a delayed transition within a single view hierarchy, follow these steps:</p>
+
+<ol>
+<li>When the event that triggers the transition occurs, call the {@link
+android.transition.TransitionManager#beginDelayedTransition
+TransitionManager.beginDelayedTransition()} method providing the parent view of all the views
+you want to change and the transition to use. The framework stores the current state of the
+child views and their property values.</li>
+<li>Make changes to the child views as required by your use case. The framework records
+the changes you make to the child views and their properties.</li>
+<li>When the system redraws the user interface according to your changes, the framework
+animates the changes between the original state and the new state.</li>
+</ol>
+
+<p>The following example shows how to animate the addition of a text view to a view hierarchy
+using a delayed transition. The first snippet shows the layout definition file:</p>
+
+<p class="code-caption">res/layout/activity_main.xml</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mainLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    &lt;EditText
+        android:id="@+id/inputText"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+    ...
+&lt;/RelativeLayout>
+</pre>
+
+<p>The next snippet shows the code that animates the addition of the text view:</p>
+
+<p class="code-caption">MainActivity.java</p>
+
+<pre>
+private TextView mLabelText;
+private Fade mFade;
+private ViewGroup mRootView;
+...
+
+// Load the layout
+this.setContentView(R.layout.activity_main);
+...
+
+// Create a new TextView and set some View properties
+mLabelText = new TextView();
+mLabelText.setText("Label").setId("1");
+
+// Get the root view and create a transition
+mRootView = (ViewGroup) findViewById(R.id.mainLayout);
+mFade = new Fade(IN);
+
+// Start recording changes to the view hierarchy
+TransitionManager.beginDelayedTransition(mRootView, mFade);
+
+// Add the new TextView to the view hierarchy
+mRootView.addView(mLabelText);
+
+// When the system redraws the screen to show this update,
+// the framework will animate the addition as a fade in
+</pre>
+
+
+
+<h2 id="Callbacks">Define Transition Lifecycle Callbacks</h2>
+
+<p>The transition lifecycle is similar to the activity lifecycle. It represents the transition
+states that the framework monitors during the time between a call to the {@link
+android.transition.TransitionManager#go TransitionManager.go()} method and the completion of
+the animation. At important lifecycle states, the framework invokes callbacks defined by
+the {@link android.transition.Transition.TransitionListener TransitionListener}
+interface.</p>
+
+<p>Transition lifecycle callbacks are useful, for example, for copying a view property value
+from the starting view hierarchy to the ending view hierarchy during a scene change. You
+cannot simply copy the value from its starting view to the view in the ending view hierarchy,
+because the ending view hierarchy is not inflated until the transition is completed.
+Instead, you need to store the value in a variable and then copy it into the ending view
+hierarchy when the framework has finished the transition. To get notified when the transition
+is completed, you can implement the {@link
+android.transition.Transition.TransitionListener#onTransitionEnd
+TransitionListener.onTransitionEnd()} method in your activity.</p>
+
+<p>For more information, see the API reference for the {@link
+android.transition.Transition.TransitionListener TransitionListener} class.</p>
diff --git a/docs/html/training/tv/playback/card.jd b/docs/html/training/tv/playback/card.jd
new file mode 100644
index 0000000..8ac75fd
--- /dev/null
+++ b/docs/html/training/tv/playback/card.jd
@@ -0,0 +1,156 @@
+page.title=Providing a Card View
+page.tags="card"
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>This lesson teaches you to</h2>
+  <ol>
+    <li><a href="#presenter">Create a Card Presenter</a></li>
+    <li><a href="#card-view">Create a Card View</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>In the previous lesson, you created a catalog browser, implemented in a browse fragment, that
+displays a list of media items. In this lesson, you create the card views for your media items and
+present them in the browse fragment.</p>
+
+<p>The {@link android.support.v17.leanback.widget.BaseCardView} class and subclasses display the meta
+data associated with a media item. The {@link android.support.v17.leanback.widget.ImageCardView}
+class used in this lesson displays an image for the content along with the media item's title.</p>
+
+<p>This lesson describes code from the <a href="https://github.com/googlesamples/androidtv-Leanback">
+Android Leanback sample app</a>, available on GitHub. Use this sample code to start your own
+app.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/app-browse.png" alt="App main screen"/>
+<p class="img-caption"><b>Figure 1.</b> The <a href="https://github.com/googlesamples/androidtv-Leanback">
+Leanback sample app</a> browse fragment with a card presenter displaying card view objects.</p>
+
+<h2 id="presenter">Create a Card Presenter</h2>
+
+<p>A {@link android.support.v17.leanback.widget.Presenter} generates views and binds objects to them
+on demand. In the browse fragment where your app presents its content to the user, you create a
+{@link android.support.v17.leanback.widget.Presenter} for the content cards and pass it to the adapter
+that adds the content to the screen. In the following code, the <code>CardPresenter</code> is created
+in the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished(android.support.v4.content.Loader, java.lang.Object) onLoadFinished()}
+callback of the {@link android.support.v4.app.LoaderManager}.</p>
+
+<pre>
+&#64;Override
+public void onLoadFinished(Loader&lt;HashMap&lt;String, List&lt;Movie&gt;&gt;&gt; arg0,
+                           HashMap&lt;String, List&lt;Movie&gt;&gt; data) {
+
+    mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+    CardPresenter cardPresenter = new CardPresenter();
+
+    int i = 0;
+
+    for (Map.Entry&lt;String, List&lt;Movie&gt;&gt; entry : data.entrySet()) {
+        ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
+        List&lt;Movie&gt; list = entry.getValue();
+
+        for (int j = 0; j &lt; list.size(); j++) {
+            listRowAdapter.add(list.get(j));
+        }
+        HeaderItem header = new HeaderItem(i, entry.getKey(), null);
+        i++;
+        mRowsAdapter.add(new ListRow(header, listRowAdapter));
+    }
+
+    HeaderItem gridHeader = new HeaderItem(i, getString(R.string.more_samples),
+            null);
+
+    GridItemPresenter gridPresenter = new GridItemPresenter();
+    ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
+    gridRowAdapter.add(getString(R.string.grid_view));
+    gridRowAdapter.add(getString(R.string.error_fragment));
+    gridRowAdapter.add(getString(R.string.personal_settings));
+    mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
+
+    setAdapter(mRowsAdapter);
+
+    updateRecommendations();
+}
+</pre>
+
+<h2 id="card-view">Create a Card View</h2>
+
+<p>In this step, you build the card presenter with a view holder for the card view that describes
+your media content items. Note that each presenter must only create one view type. If you have two
+different card view types then you need two different card presenters.</p>
+
+<p>In the {@link android.support.v17.leanback.widget.Presenter}, implement an
+{@link android.support.v17.leanback.widget.Presenter#onCreateViewHolder(android.view.ViewGroup) onCreateViewHolder()}
+callback that creates a view holder that can be used to display a content item.</p>
+
+<pre>
+&#64;Override
+public class CardPresenter extends Presenter {
+
+    private Context mContext;
+    private static int CARD_WIDTH = 313;
+    private static int CARD_HEIGHT = 176;
+    private Drawable mDefaultCardImage;
+
+    &#64;Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent) {
+        mContext = parent.getContext();
+        mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
+...
+</pre>
+
+<p>In the {@link android.support.v17.leanback.widget.Presenter#onCreateViewHolder(android.view.ViewGroup)
+onCreateViewHolder()} method, create a card view for content items. The sample below uses an
+{@link android.support.v17.leanback.widget.ImageCardView}.</p>
+
+<p>When a card is selected, the default behavior expands it to a larger size. If you want to designate
+a different color for the selected card, call {@link android.support.v17.leanback.widget.BaseCardView#setSelected(boolean)
+setSelected()}
+as shown here.</p>
+
+<pre>
+...
+    ImageCardView cardView = new ImageCardView(mContext) {
+        &#64;Override
+        public void setSelected(boolean selected) {
+            int selected_background = mContext.getResources().getColor(R.color.detail_background);
+            int default_background = mContext.getResources().getColor(R.color.default_background);
+            int color = selected ? selected_background : default_background;
+            findViewById(R.id.info_field).setBackgroundColor(color);
+            super.setSelected(selected);
+        }
+    };
+...
+</pre>
+
+<p>When the user opens your app, the {@link android.support.v17.leanback.widget.Presenter.ViewHolder}
+displays the <code>CardView</code> objects for your content items. You need to set these to receive
+focus from the D-pad controller by calling {@link android.view.View#setFocusable(boolean) setFocusable(true)}
+and {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode(true)}.</p>
+
+<pre>
+...
+    cardView.setFocusable(true);
+    cardView.setFocusableInTouchMode(true);
+    return new ViewHolder(cardView);
+}
+</pre>
+
+<p>When the user selects the {@link android.support.v17.leanback.widget.ImageCardView}, it expands
+to reveal its text area with the background color you specify, as shown in figure 2.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/card-view.png" alt="App card view"/>
+<p class="img-caption"><b>Figure 2.</b> The <a href="https://github.com/googlesamples/androidtv-Leanback">
+Leanback sample app</a> image card view when selected.</p>
+
+
diff --git a/docs/html/training/tv/playback/index.jd b/docs/html/training/tv/playback/index.jd
index 5427d48..0e9c5ec 100644
--- a/docs/html/training/tv/playback/index.jd
+++ b/docs/html/training/tv/playback/index.jd
@@ -56,6 +56,9 @@
     <dd>Learn how to use the Leanback support library to build a browsing interface for media
       catalogs.</dd>
 
+  <dt><b><a href="details.html">Providing a Card View</a></b></dt>
+    <dd>Learn how to use the Leanback support library to build a card view for content items.</dd>
+
   <dt><b><a href="details.html">Building a Details View</a></b></dt>
     <dd>Learn how to use the Leanback support library to build a details page for media items.</dd>
 
diff --git a/docs/html/training/tv/start/layouts.jd b/docs/html/training/tv/start/layouts.jd
index 177ea7a..a378096 100644
--- a/docs/html/training/tv/start/layouts.jd
+++ b/docs/html/training/tv/start/layouts.jd
@@ -119,8 +119,8 @@
 
 <p>
   Avoid screen elements being clipped due to overscan and by incorporating a 10% margin
-  on all sides of your layout. This translates into a 27dp margin on the left and right edges and
-  a 48dp margin on the top and bottom of your base layouts for activities. The following
+  on all sides of your layout. This translates into a 48dp margin on the left and right edges and
+  a 27dp margin on the top and bottom of your base layouts for activities. The following
   example layout demonstrates how to set these margins in the root layout for a TV app:
 </p>
 
diff --git a/graphics/java/android/graphics/AvoidXfermode.java b/graphics/java/android/graphics/AvoidXfermode.java
index 206c959..48ee6fa 100644
--- a/graphics/java/android/graphics/AvoidXfermode.java
+++ b/graphics/java/android/graphics/AvoidXfermode.java
@@ -23,7 +23,7 @@
 @Deprecated
 public class AvoidXfermode extends Xfermode {
 
-    // these need to match the enum in SkAvoidXfermode.h on the native side
+    // these need to match the enum in AvoidXfermode.h on the native side
     public enum Mode {
         AVOID   (0),    //!< draw everywhere except on the opColor
         TARGET  (1);    //!< draw only on top of the opColor
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index fa9af2a..39272b9 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -122,11 +122,6 @@
      * @param canvas  The picture is drawn to this canvas
      */
     public void draw(Canvas canvas) {
-        if (canvas.isHardwareAccelerated()) {
-            throw new IllegalArgumentException(
-                    "Picture playback is only supported on software canvas.");
-        }
-
         if (mRecordingCanvas != null) {
             endRecording();
         }
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 8b70a08..50a6df4 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -34,7 +34,6 @@
 import android.graphics.Outline;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 
@@ -60,28 +59,49 @@
 public class InsetDrawable extends Drawable implements Drawable.Callback {
     private final Rect mTmpRect = new Rect();
 
-    private final InsetState mState;
+    private InsetState mState;
+    private Drawable mDrawable;
 
     private boolean mMutated;
 
-    /*package*/ InsetDrawable() {
-        this(null, null);
+    /**
+     * No-arg constructor used by drawable inflation.
+     */
+    InsetDrawable() {
+        this(new InsetState(), null);
     }
 
+    /**
+     * Creates a new inset drawable with the specified inset.
+     *
+     * @param drawable The drawable to inset.
+     * @param inset Inset in pixels around the drawable.
+     */
     public InsetDrawable(Drawable drawable, int inset) {
         this(drawable, inset, inset, inset, inset);
     }
 
-    public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
-                         int insetRight, int insetBottom) {
-        this(null, null);
+    /**
+     * Creates a new inset drawable with the specified insets.
+     *
+     * @param drawable The drawable to inset.
+     * @param insetLeft Left inset in pixels.
+     * @param insetTop Top inset in pixels.
+     * @param insetRight Right inset in pixels.
+     * @param insetBottom Bottom inset in pixels.
+     */
+    public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,int insetRight,
+            int insetBottom) {
+        this(new InsetState(), null);
 
-        mState.mDrawable = drawable;
+        mState.mDrawableState = drawable == null ? null : drawable.getConstantState();
         mState.mInsetLeft = insetLeft;
         mState.mInsetTop = insetTop;
         mState.mInsetRight = insetRight;
         mState.mInsetBottom = insetBottom;
 
+        mDrawable = drawable;
+
         if (drawable != null) {
             drawable.setCallback(this);
         }
@@ -93,38 +113,53 @@
         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
         super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible);
 
-        // Reset mDrawable to preserve old multiple-inflate behavior. This is
-        // silly, but we have CTS tests that rely on it.
-        mState.mDrawable = null;
-
         updateStateFromTypedArray(a);
         inflateChildElements(r, parser, attrs, theme);
         verifyRequiredAttributes(a);
         a.recycle();
     }
 
+    private void setDrawable(Drawable dr) {
+        if (mDrawable != null) {
+            mDrawable.setCallback(null);
+        }
+
+        mDrawable = dr;
+
+        if (dr != null) {
+            dr.setCallback(this);
+            dr.setVisible(isVisible(), true);
+            dr.setState(getState());
+            dr.setLevel(getLevel());
+            dr.setBounds(getBounds());
+            dr.setLayoutDirection(getLayoutDirection());
+        }
+    }
+
     private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
             Theme theme) throws XmlPullParserException, IOException {
         // Load inner XML elements.
-        if (mState.mDrawable == null) {
+        if (mDrawable == null) {
             int type;
             while ((type=parser.next()) == XmlPullParser.TEXT) {
             }
             if (type != XmlPullParser.START_TAG) {
-                throw new XmlPullParserException(
-                        parser.getPositionDescription()
-                                + ": <inset> tag requires a 'drawable' attribute or "
-                                + "child tag defining a drawable");
+                throw new XmlPullParserException(parser.getPositionDescription()
+                        + ": <inset> tag requires a 'drawable' attribute or "
+                        + "child tag defining a drawable");
             }
+
             final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
-            mState.mDrawable = dr;
-            dr.setCallback(this);
+            if (dr != null) {
+                mState.mDrawableState = dr.getConstantState();
+                setDrawable(dr);
+            }
         }
     }
 
     private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
         // If we're not waiting on a theme, verify required attributes.
-        if (mState.mDrawable == null && (mState.mThemeAttrs == null
+        if (mDrawable == null && (mState.mThemeAttrs == null
                 || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
             throw new XmlPullParserException(a.getPositionDescription()
                     + ": <inset> tag requires a 'drawable' attribute or "
@@ -148,8 +183,8 @@
                 case R.styleable.InsetDrawable_drawable:
                     final Drawable dr = a.getDrawable(attr);
                     if (dr != null) {
-                        state.mDrawable = dr;
-                        dr.setCallback(this);
+                        mState.mDrawableState = dr.getConstantState();
+                        setDrawable(dr);
                     }
                     break;
                 case R.styleable.InsetDrawable_inset:
@@ -198,8 +233,8 @@
             }
         }
 
-        if (state.mDrawable != null && state.mDrawable.canApplyTheme()) {
-            state.mDrawable.applyTheme(t);
+        if (mDrawable != null && mDrawable.canApplyTheme()) {
+            mDrawable.applyTheme(t);
         }
     }
 
@@ -234,27 +269,27 @@
 
     @Override
     public void draw(Canvas canvas) {
-        mState.mDrawable.draw(canvas);
+        mDrawable.draw(canvas);
     }
 
     @Override
     public int getChangingConfigurations() {
         return super.getChangingConfigurations()
                 | mState.mChangingConfigurations
-                | mState.mDrawable.getChangingConfigurations();
+                | mDrawable.getChangingConfigurations();
     }
 
     @Override
     public boolean getPadding(Rect padding) {
-        boolean pad = mState.mDrawable.getPadding(padding);
+        final boolean pad = mDrawable.getPadding(padding);
 
         padding.left += mState.mInsetLeft;
         padding.right += mState.mInsetRight;
         padding.top += mState.mInsetTop;
         padding.bottom += mState.mInsetBottom;
 
-        return pad || (mState.mInsetLeft | mState.mInsetRight |
-                mState.mInsetTop | mState.mInsetBottom) != 0;
+        return pad || (mState.mInsetLeft | mState.mInsetRight
+                | mState.mInsetTop | mState.mInsetBottom) != 0;
     }
 
     /** @hide */
@@ -269,61 +304,61 @@
 
     @Override
     public void setHotspot(float x, float y) {
-        mState.mDrawable.setHotspot(x, y);
+        mDrawable.setHotspot(x, y);
     }
 
     @Override
     public void setHotspotBounds(int left, int top, int right, int bottom) {
-        mState.mDrawable.setHotspotBounds(left, top, right, bottom);
+        mDrawable.setHotspotBounds(left, top, right, bottom);
     }
 
     /** @hide */
     @Override
     public void getHotspotBounds(Rect outRect) {
-        mState.mDrawable.getHotspotBounds(outRect);
+        mDrawable.getHotspotBounds(outRect);
     }
 
     @Override
     public boolean setVisible(boolean visible, boolean restart) {
-        mState.mDrawable.setVisible(visible, restart);
+        mDrawable.setVisible(visible, restart);
         return super.setVisible(visible, restart);
     }
 
     @Override
     public void setAlpha(int alpha) {
-        mState.mDrawable.setAlpha(alpha);
+        mDrawable.setAlpha(alpha);
     }
 
     @Override
     public int getAlpha() {
-        return mState.mDrawable.getAlpha();
+        return mDrawable.getAlpha();
     }
 
     @Override
     public void setColorFilter(ColorFilter cf) {
-        mState.mDrawable.setColorFilter(cf);
+        mDrawable.setColorFilter(cf);
     }
 
     @Override
     public void setTintList(ColorStateList tint) {
-        mState.mDrawable.setTintList(tint);
+        mDrawable.setTintList(tint);
     }
 
     @Override
     public void setTintMode(Mode tintMode) {
-        mState.mDrawable.setTintMode(tintMode);
+        mDrawable.setTintMode(tintMode);
     }
 
     /** {@hide} */
     @Override
     public void setLayoutDirection(int layoutDirection) {
-        mState.mDrawable.setLayoutDirection(layoutDirection);
+        mDrawable.setLayoutDirection(layoutDirection);
     }
 
     @Override
     public int getOpacity() {
         final InsetState state = mState;
-        final int opacity = state.mDrawable.getOpacity();
+        final int opacity = mDrawable.getOpacity();
         if (opacity == PixelFormat.OPAQUE && (state.mInsetLeft > 0 || state.mInsetTop > 0
                 || state.mInsetRight > 0 || state.mInsetBottom > 0)) {
             return PixelFormat.TRANSLUCENT;
@@ -333,19 +368,19 @@
 
     @Override
     public boolean isStateful() {
-        return mState.mDrawable.isStateful();
+        return mDrawable.isStateful();
     }
 
     @Override
     protected boolean onStateChange(int[] state) {
-        boolean changed = mState.mDrawable.setState(state);
+        final boolean changed = mDrawable.setState(state);
         onBoundsChange(getBounds());
         return changed;
     }
 
     @Override
     protected boolean onLevelChange(int level) {
-        return mState.mDrawable.setLevel(level);
+        return mDrawable.setLevel(level);
     }
 
     @Override
@@ -358,24 +393,22 @@
         r.right -= mState.mInsetRight;
         r.bottom -= mState.mInsetBottom;
 
-        mState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
+        mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
     }
 
     @Override
     public int getIntrinsicWidth() {
-        return mState.mDrawable.getIntrinsicWidth()
-                + mState.mInsetLeft + mState.mInsetRight;
+        return mDrawable.getIntrinsicWidth() + mState.mInsetLeft + mState.mInsetRight;
     }
 
     @Override
     public int getIntrinsicHeight() {
-        return mState.mDrawable.getIntrinsicHeight()
-                + mState.mInsetTop + mState.mInsetBottom;
+        return mDrawable.getIntrinsicHeight() + mState.mInsetTop + mState.mInsetBottom;
     }
 
     @Override
     public void getOutline(@NonNull Outline outline) {
-        mState.mDrawable.getOutline(outline);
+        mDrawable.getOutline(outline);
     }
 
     @Override
@@ -390,7 +423,9 @@
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
-            mState.mDrawable.mutate();
+            mState = new InsetState(mState);
+            mDrawable.mutate();
+            mState.mDrawableState = mDrawable.getConstantState();
             mMutated = true;
         }
         return this;
@@ -401,7 +436,7 @@
      */
     public void clearMutated() {
         super.clearMutated();
-        mState.mDrawable.clearMutated();
+        mDrawable.clearMutated();
         mMutated = false;
     }
 
@@ -409,51 +444,53 @@
      * Returns the drawable wrapped by this InsetDrawable. May be null.
      */
     public Drawable getDrawable() {
-        return mState.mDrawable;
+        return mDrawable;
     }
 
-    final static class InsetState extends ConstantState {
+    private static final class InsetState extends ConstantState {
         int[] mThemeAttrs;
         int mChangingConfigurations;
 
-        Drawable mDrawable;
+        ConstantState mDrawableState;
 
         int mInsetLeft = 0;
         int mInsetTop = 0;
         int mInsetRight = 0;
         int mInsetBottom = 0;
 
-        private boolean mCheckedConstantState;
-        private boolean mCanConstantState;
+        public InsetState() {
+            // Empty constructor.
+        }
 
-        InsetState(InsetState orig, InsetDrawable owner, Resources res) {
+        public InsetState(InsetState orig) {
             if (orig != null) {
                 mThemeAttrs = orig.mThemeAttrs;
                 mChangingConfigurations = orig.mChangingConfigurations;
-                if (res != null) {
-                    mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
-                } else {
-                    mDrawable = orig.mDrawable.getConstantState().newDrawable();
-                }
-                mDrawable.setCallback(owner);
-                mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
-                mDrawable.setBounds(orig.mDrawable.getBounds());
-                mDrawable.setLevel(orig.mDrawable.getLevel());
+                mDrawableState = orig.mDrawableState;
                 mInsetLeft = orig.mInsetLeft;
                 mInsetTop = orig.mInsetTop;
                 mInsetRight = orig.mInsetRight;
                 mInsetBottom = orig.mInsetBottom;
-                mCheckedConstantState = mCanConstantState = true;
             }
         }
 
         @Override
         public boolean canApplyTheme() {
-            return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme())
+            return mThemeAttrs != null
+                    || (mDrawableState != null && mDrawableState.canApplyTheme())
                     || super.canApplyTheme();
         }
 
         @Override
+        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
+            final ConstantState state = mDrawableState;
+            if (state != null) {
+                return state.addAtlasableBitmaps(atlasList);
+            }
+            return 0;
+        }
+
+        @Override
         public Drawable newDrawable() {
             return new InsetDrawable(this, null);
         }
@@ -468,27 +505,31 @@
             return mChangingConfigurations;
         }
 
-        boolean canConstantState() {
-            if (!mCheckedConstantState) {
-                mCanConstantState = mDrawable.getConstantState() != null;
-                mCheckedConstantState = true;
-            }
-
-            return mCanConstantState;
-        }
-
-        @Override
-        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
-            final ConstantState state = mDrawable.getConstantState();
-            if (state != null) {
-                return state.addAtlasableBitmaps(atlasList);
-            }
-            return 0;
+        public boolean canConstantState() {
+            return mDrawableState != null;
         }
     }
 
+    /**
+     * The one constructor to rule them all. This is called by all public
+     * constructors to set the state and initialize local properties.
+     */
     private InsetDrawable(InsetState state, Resources res) {
-        mState = new InsetState(state, this, res);
+        mState = state;
+
+        updateLocalState(res);
+    }
+
+    /**
+     * Initializes local dynamic properties from state. This should be called
+     * after significant state changes, e.g. from the One True Constructor and
+     * after inflating or applying a theme.
+     */
+    private void updateLocalState(Resources res) {
+        if (mState.mDrawableState != null) {
+            final Drawable dr = mState.mDrawableState.newDrawable(res);
+            setDrawable(dr);
+        }
     }
 }
 
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index daf4427..505bd1d 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -29,6 +29,8 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.LayoutDirection;
+import android.view.Gravity;
 import android.view.View;
 
 import com.android.internal.R;
@@ -54,6 +56,11 @@
  * @attr ref android.R.styleable#LayerDrawableItem_top
  * @attr ref android.R.styleable#LayerDrawableItem_right
  * @attr ref android.R.styleable#LayerDrawableItem_bottom
+ * @attr ref android.R.styleable#LayerDrawableItem_start
+ * @attr ref android.R.styleable#LayerDrawableItem_end
+ * @attr ref android.R.styleable#LayerDrawableItem_width
+ * @attr ref android.R.styleable#LayerDrawableItem_height
+ * @attr ref android.R.styleable#LayerDrawableItem_gravity
  * @attr ref android.R.styleable#LayerDrawableItem_drawable
  * @attr ref android.R.styleable#LayerDrawableItem_id
 */
@@ -73,6 +80,9 @@
      */
     public static final int PADDING_MODE_STACK = 1;
 
+    /** Value used for undefined start and end insets. */
+    private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
+
     LayerState mLayerState;
 
     private int mOpacityOverride = PixelFormat.UNKNOWN;
@@ -231,6 +241,16 @@
                 R.styleable.LayerDrawableItem_right, layer.mInsetR);
         layer.mInsetB = a.getDimensionPixelOffset(
                 R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
+        layer.mInsetS = a.getDimensionPixelOffset(
+                R.styleable.LayerDrawableItem_start, layer.mInsetS);
+        layer.mInsetE = a.getDimensionPixelOffset(
+                R.styleable.LayerDrawableItem_end, layer.mInsetE);
+        layer.mWidth = a.getDimensionPixelSize(
+                R.styleable.LayerDrawableItem_width, layer.mWidth);
+        layer.mHeight = a.getDimensionPixelSize(
+                R.styleable.LayerDrawableItem_height, layer.mHeight);
+        layer.mGravity = a.getInteger(
+                R.styleable.LayerDrawableItem_gravity, layer.mGravity);
         layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
 
         final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
@@ -447,6 +467,89 @@
     }
 
     /**
+     * Sets an explicit size for the specified layer.
+     * <p>
+     * <strong>Note:</strong> Setting an explicit layer size changes the
+     * default layer gravity behavior. See {@link #setLayerGravity(int, int)}
+     * for more information.
+     *
+     * @param index the index of the drawable to adjust
+     * @param w width in pixels, or -1 to use the intrinsic width
+     * @param h height in pixels, or -1 to use the intrinsic height
+     *
+     * @see #getLayerWidth(int)
+     * @see #getLayerHeight(int)
+     * @attr ref android.R.styleable#LayerDrawableItem_width
+     * @attr ref android.R.styleable#LayerDrawableItem_height
+     */
+    public void setLayerSize(int index, int w, int h) {
+        final ChildDrawable childDrawable = mLayerState.mChildren[index];
+        childDrawable.mWidth = w;
+        childDrawable.mHeight = h;
+    }
+
+    /**
+     * @param index the index of the drawable to adjust
+     * @return the explicit width of the layer, or -1 if not specified
+     *
+     * @see #setLayerSize(int, int, int)
+     * @attr ref android.R.styleable#LayerDrawableItem_width
+     */
+    public int getLayerWidth(int index) {
+        final ChildDrawable childDrawable = mLayerState.mChildren[index];
+        return childDrawable.mWidth;
+    }
+
+    /**
+     * @param index the index of the drawable to adjust
+     * @return the explicit height of the layer, or -1 if not specified
+     *
+     * @see #setLayerSize(int, int, int)
+     * @attr ref android.R.styleable#LayerDrawableItem_height
+     */
+    public int getLayerHeight(int index) {
+        final ChildDrawable childDrawable = mLayerState.mChildren[index];
+        return childDrawable.mHeight;
+    }
+
+    /**
+     * Sets the gravity used to position or stretch the specified layer within
+     * its container. Gravity is applied after any layer insets (see
+     * {@link #setLayerInset(int, int, int, int, int)}) or padding (see
+     * {@link #setPaddingMode(int)}).
+     * <p>
+     * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default
+     * behavior depends on whether an explicit width or height has been set
+     * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set,
+     * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or
+     * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction
+     * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}.
+     *
+     * @param index the index of the drawable to adjust
+     * @param gravity the gravity to set for the layer
+     *
+     * @see #getLayerGravity(int)
+     * @attr ref android.R.styleable#LayerDrawableItem_gravity
+     */
+    public void setLayerGravity(int index, int gravity) {
+        final ChildDrawable childDrawable = mLayerState.mChildren[index];
+        childDrawable.mGravity = gravity;
+    }
+
+    /**
+     * @param index the index of the layer
+     * @return the gravity used to position or stretch the specified layer
+     *         within its container
+     *
+     * @see #setLayerGravity(int, int)
+     * @attr ref android.R.styleable#LayerDrawableItem_gravity
+     */
+    public int getLayerGravity(int index) {
+        final ChildDrawable childDrawable = mLayerState.mChildren[index];
+        return childDrawable.mGravity;
+    }
+
+    /**
      * Specifies the insets in pixels for the drawable at the specified index.
      *
      * @param index the index of the drawable to adjust
@@ -454,13 +557,43 @@
      * @param t number of pixels to add to the top bound
      * @param r number of pixels to subtract from the right bound
      * @param b number of pixels to subtract from the bottom bound
+     *
+     * @attr ref android.R.styleable#LayerDrawableItem_left
+     * @attr ref android.R.styleable#LayerDrawableItem_top
+     * @attr ref android.R.styleable#LayerDrawableItem_right
+     * @attr ref android.R.styleable#LayerDrawableItem_bottom
      */
     public void setLayerInset(int index, int l, int t, int r, int b) {
+        setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET);
+    }
+
+    /**
+     * Specifies the relative insets in pixels for the drawable at the
+     * specified index.
+     *
+     * @param index the index of the drawable to adjust
+     * @param s number of pixels to inset from the start bound
+     * @param t number of pixels to inset from the top bound
+     * @param e number of pixels to inset from the end bound
+     * @param b number of pixels to inset from the bottom bound
+     *
+     * @attr ref android.R.styleable#LayerDrawableItem_start
+     * @attr ref android.R.styleable#LayerDrawableItem_top
+     * @attr ref android.R.styleable#LayerDrawableItem_end
+     * @attr ref android.R.styleable#LayerDrawableItem_bottom
+     */
+    public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
+        setLayerInsetInternal(index, 0, t, 0, b, s, e);
+    }
+
+    private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) {
         final ChildDrawable childDrawable = mLayerState.mChildren[index];
         childDrawable.mInsetL = l;
         childDrawable.mInsetT = t;
         childDrawable.mInsetR = r;
         childDrawable.mInsetB = b;
+        childDrawable.mInsetS = s;
+        childDrawable.mInsetE = e;
     }
 
     /**
@@ -770,7 +903,7 @@
         }
 
         if (paddingChanged) {
-            onBoundsChange(getBounds());
+            updateLayerBounds(getBounds());
         }
 
         return changed;
@@ -795,7 +928,7 @@
         }
 
         if (paddingChanged) {
-            onBoundsChange(getBounds());
+            updateLayerBounds(getBounds());
         }
 
         return changed;
@@ -803,18 +936,50 @@
 
     @Override
     protected void onBoundsChange(Rect bounds) {
+        updateLayerBounds(bounds);
+    }
+
+    private void updateLayerBounds(Rect bounds) {
         int padL = 0;
         int padT = 0;
         int padR = 0;
         int padB = 0;
 
+        final Rect outRect = mTmpRect;
+        final int layoutDirection = getLayoutDirection();
         final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
         final ChildDrawable[] array = mLayerState.mChildren;
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
             final ChildDrawable r = array[i];
-            r.mDrawable.setBounds(bounds.left + r.mInsetL + padL, bounds.top + r.mInsetT + padT,
-                    bounds.right - r.mInsetR - padR, bounds.bottom - r.mInsetB - padB);
+            final Drawable d = r.mDrawable;
+            final Rect container = d.getBounds();
+
+            // Take the resolved layout direction into account. If start / end
+            // padding are defined, they will be resolved (hence overriding) to
+            // left / right or right / left depending on the resolved layout
+            // direction. If start / end padding are not defined, use the
+            // left / right ones.
+            final int insetL, insetR;
+            if (layoutDirection == LayoutDirection.RTL) {
+                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
+                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
+            } else {
+                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
+                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
+            }
+
+            // Establish containing region based on aggregate padding and
+            // requested insets for the current layer.
+            container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
+                    bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
+
+            // Apply resolved gravity to drawable based on resolved size.
+            final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight);
+            final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
+            final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
+            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
+            d.setBounds(outRect);
 
             if (nest) {
                 padL += mPaddingL[i];
@@ -825,6 +990,38 @@
         }
     }
 
+    /**
+     * Resolves layer gravity given explicit gravity and dimensions.
+     * <p>
+     * If the client hasn't specified a gravity but has specified an explicit
+     * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
+     * preserve legacy behavior.
+     *
+     * @param gravity
+     * @param width
+     * @param height
+     * @return
+     */
+    private int resolveGravity(int gravity, int width, int height) {
+        if (!Gravity.isHorizontal(gravity)) {
+            if (width < 0) {
+                gravity |= Gravity.FILL_HORIZONTAL;
+            } else {
+                gravity |= Gravity.START;
+            }
+        }
+
+        if (!Gravity.isVertical(gravity)) {
+            if (height < 0) {
+                gravity |= Gravity.FILL_VERTICAL;
+            } else {
+                gravity |= Gravity.TOP;
+            }
+        }
+
+        return gravity;
+    }
+
     @Override
     public int getIntrinsicWidth() {
         int width = -1;
@@ -836,7 +1033,8 @@
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
             final ChildDrawable r = array[i];
-            final int w = r.mDrawable.getIntrinsicWidth() + r.mInsetL + r.mInsetR + padL + padR;
+            final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
+            final int w = minWidth + r.mInsetL + r.mInsetR + padL + padR;
             if (w > width) {
                 width = w;
             }
@@ -861,7 +1059,8 @@
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
             final ChildDrawable r = array[i];
-            int h = r.mDrawable.getIntrinsicHeight() + r.mInsetT + r.mInsetB + padT + padB;
+            final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
+            final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
             if (h > height) {
                 height = h;
             }
@@ -948,18 +1147,24 @@
     /** @hide */
     @Override
     public void setLayoutDirection(int layoutDirection) {
+        super.setLayoutDirection(layoutDirection);
         final ChildDrawable[] array = mLayerState.mChildren;
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
             array[i].mDrawable.setLayoutDirection(layoutDirection);
         }
-        super.setLayoutDirection(layoutDirection);
+        updateLayerBounds(getBounds());
     }
 
     static class ChildDrawable {
         public Drawable mDrawable;
         public int[] mThemeAttrs;
         public int mInsetL, mInsetT, mInsetR, mInsetB;
+        public int mInsetS = UNDEFINED_INSET;
+        public int mInsetE = UNDEFINED_INSET;
+        public int mWidth = -1;
+        public int mHeight = -1;
+        public int mGravity = Gravity.NO_GRAVITY;
         public int mId = View.NO_ID;
 
         ChildDrawable() {
@@ -981,6 +1186,11 @@
             mInsetT = orig.mInsetT;
             mInsetR = orig.mInsetR;
             mInsetB = orig.mInsetB;
+            mInsetS = orig.mInsetS;
+            mInsetE = orig.mInsetE;
+            mWidth = orig.mWidth;
+            mHeight = orig.mHeight;
+            mGravity = orig.mGravity;
             mId = orig.mId;
         }
     }
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 421c5c1..8673219 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -118,9 +118,9 @@
                        yDivsOffset(0), colorsOffset(0) { }
 
     int8_t wasDeserialized;
-    int8_t numXDivs;
-    int8_t numYDivs;
-    int8_t numColors;
+    uint8_t numXDivs;
+    uint8_t numYDivs;
+    uint8_t numColors;
 
     // The offset (from the start of this structure) to the xDivs & yDivs
     // array for this 9patch. To get a pointer to this array, call
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 6ec42c2..0a210d6 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -179,7 +179,7 @@
 void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
         const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d,
         float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
-    shadowVertexBuffer.setMode(VertexBuffer::kIndices);
+    shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices);
 
     // In order to computer the outer vertices in one loop, we need pre-compute
     // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index b1673fe..e05dd55 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -5,12 +5,26 @@
     -Wno-gnu-static-float-init
 
 LOCAL_SRC_FILES := \
+    font/CacheTexture.cpp \
+    font/Font.cpp \
+    renderstate/Blend.cpp \
+    renderstate/MeshState.cpp \
+    renderstate/PixelBufferState.cpp \
+    renderstate/RenderState.cpp \
+    renderstate/Scissor.cpp \
+    renderstate/Stencil.cpp \
+    renderstate/TextureState.cpp \
+    renderthread/CanvasContext.cpp \
+    renderthread/DrawFrameTask.cpp \
+    renderthread/EglManager.cpp \
+    renderthread/RenderProxy.cpp \
+    renderthread/RenderTask.cpp \
+    renderthread/RenderThread.cpp \
+    renderthread/TimeLord.cpp \
+    thread/TaskManager.cpp \
     utils/Blur.cpp \
     utils/GLUtils.cpp \
     utils/SortedListImpl.cpp \
-    thread/TaskManager.cpp \
-    font/CacheTexture.cpp \
-    font/Font.cpp \
     AmbientShadow.cpp \
     AnimationContext.cpp \
     Animator.cpp \
@@ -20,9 +34,9 @@
     CanvasState.cpp \
     ClipArea.cpp \
     DamageAccumulator.cpp \
-    DisplayList.cpp \
     DeferredDisplayList.cpp \
     DeferredLayerUpdater.cpp \
+    DisplayList.cpp \
     DisplayListRenderer.cpp \
     Dither.cpp \
     DrawProfiler.cpp \
@@ -30,6 +44,7 @@
     FboCache.cpp \
     FontRenderer.cpp \
     GammaFontRenderer.cpp \
+    GlopBuilder.cpp \
     GradientCache.cpp \
     Image.cpp \
     Interpolator.cpp \
@@ -48,28 +63,17 @@
     RenderBufferCache.cpp \
     RenderNode.cpp \
     RenderProperties.cpp \
-    RenderState.cpp \
     ResourceCache.cpp \
     ShadowTessellator.cpp \
     SkiaCanvas.cpp \
+    SkiaCanvasProxy.cpp \
     SkiaShader.cpp \
     Snapshot.cpp \
     SpotShadow.cpp \
-    Stencil.cpp \
     TessellationCache.cpp \
+    TextDropShadowCache.cpp \
     Texture.cpp \
-    TextureCache.cpp \
-    TextDropShadowCache.cpp
-
-# RenderThread stuff
-LOCAL_SRC_FILES += \
-    renderthread/CanvasContext.cpp \
-    renderthread/DrawFrameTask.cpp \
-    renderthread/EglManager.cpp \
-    renderthread/RenderProxy.cpp \
-    renderthread/RenderTask.cpp \
-    renderthread/RenderThread.cpp \
-    renderthread/TimeLord.cpp
+    TextureCache.cpp
 
 intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
 
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 107eb08..f4fc068 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -16,24 +16,23 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+#include "Caches.h"
+
+#include "DisplayListRenderer.h"
+#include "GammaFontRenderer.h"
+#include "LayerRenderer.h"
+#include "Properties.h"
+#include "renderstate/RenderState.h"
+#include "ShadowTessellator.h"
+
 #include <utils/Log.h>
 #include <utils/String8.h>
 
-#include "Caches.h"
-#include "DisplayListRenderer.h"
-#include "GammaFontRenderer.h"
-#include "Properties.h"
-#include "LayerRenderer.h"
-#include "ShadowTessellator.h"
-#include "RenderState.h"
-
 namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
-
 namespace uirenderer {
 
+Caches* Caches::sInstance = nullptr;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Macros
 ///////////////////////////////////////////////////////////////////////////////
@@ -48,8 +47,14 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Caches::Caches(): Singleton<Caches>(),
-        mExtensions(Extensions::getInstance()), mInitialized(false), mRenderState(nullptr) {
+Caches::Caches(RenderState& renderState)
+        : gradientCache(mExtensions)
+        , patchCache(renderState)
+        , programCache(mExtensions)
+        , dither(*this)
+        , mRenderState(&renderState)
+        , mInitialized(false) {
+    INIT_LOGD("Creating OpenGL renderer caches");
     init();
     initFont();
     initConstraints();
@@ -59,7 +64,8 @@
     initTempProperties();
 
     mDebugLevel = readDebugLevel();
-    ALOGD("Enabling debug mode %d", mDebugLevel);
+    ALOGD_IF(mDebugLevel != kDebugDisabled,
+            "Enabling debug mode %d", mDebugLevel);
 }
 
 bool Caches::init() {
@@ -67,33 +73,8 @@
 
     ATRACE_NAME("Caches::init");
 
-    glGenBuffers(1, &meshBuffer);
-    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
-
-    mCurrentBuffer = meshBuffer;
-    mCurrentIndicesBuffer = 0;
-    mCurrentPositionPointer = this;
-    mCurrentPositionStride = 0;
-    mCurrentTexCoordsPointer = this;
-    mCurrentPixelBuffer = 0;
-
-    mTexCoordsArrayEnabled = false;
-
-    glDisable(GL_SCISSOR_TEST);
-    scissorEnabled = false;
-    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-
-    glActiveTexture(gTextureUnits[0]);
-    mTextureUnit = 0;
-
     mRegionMesh = nullptr;
-    mMeshIndices = 0;
-    mShadowStripsIndices = 0;
-    blend = false;
-    lastSrcMode = GL_ZERO;
-    lastDstMode = GL_ZERO;
-    currentProgram = nullptr;
+    mProgram = nullptr;
 
     mFunctorsCount = 0;
 
@@ -101,11 +82,12 @@
     debugOverdraw = false;
     debugStencilClip = kStencilHide;
 
-    patchCache.init(*this);
+    patchCache.init();
 
     mInitialized = true;
 
-    resetBoundTextures();
+    mPixelBufferState = new PixelBufferState();
+    mTextureState = new TextureState();
 
     return true;
 }
@@ -136,12 +118,6 @@
 }
 
 void Caches::initConstraints() {
-    GLint maxTextureUnits;
-    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
-    if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
-        ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
-    }
-
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
 }
 
@@ -212,36 +188,47 @@
         INIT_LOGD("  Draw reorder enabled");
     }
 
-    return (prevDebugLayersUpdates != debugLayersUpdates) ||
-            (prevDebugOverdraw != debugOverdraw) ||
-            (prevDebugStencilClip != debugStencilClip);
+    return (prevDebugLayersUpdates != debugLayersUpdates)
+            || (prevDebugOverdraw != debugOverdraw)
+            || (prevDebugStencilClip != debugStencilClip);
 }
 
 void Caches::terminate() {
     if (!mInitialized) return;
-
-    glDeleteBuffers(1, &meshBuffer);
-    mCurrentBuffer = 0;
-
-    glDeleteBuffers(1, &mMeshIndices);
-    mMeshIndices = 0;
     mRegionMesh.release();
 
-    glDeleteBuffers(1, &mShadowStripsIndices);
-    mShadowStripsIndices = 0;
-
     fboCache.clear();
 
     programCache.clear();
-    currentProgram = nullptr;
+    mProgram = nullptr;
 
     patchCache.clear();
 
     clearGarbage();
 
+    delete mPixelBufferState;
+    mPixelBufferState = nullptr;
+    delete mTextureState;
+    mTextureState = nullptr;
     mInitialized = false;
 }
 
+void Caches::setProgram(const ProgramDescription& description) {
+    setProgram(programCache.get(description));
+}
+
+void Caches::setProgram(Program* program) {
+    if (!program || !program->isInUse()) {
+        if (mProgram) {
+            mProgram->remove();
+        }
+        if (program) {
+            program->use();
+        }
+        mProgram = program;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Debug
 ///////////////////////////////////////////////////////////////////////////////
@@ -271,7 +258,7 @@
             layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount());
     if (mRenderState) {
         int memused = 0;
-        for (std::set<const Layer*>::iterator it = mRenderState->mActiveLayers.begin();
+        for (std::set<Layer*>::iterator it = mRenderState->mActiveLayers.begin();
                 it != mRenderState->mActiveLayers.end(); it++) {
             const Layer* layer = *it;
             log.appendFormat("    Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n",
@@ -369,281 +356,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// VBO
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::bindMeshBuffer() {
-    return bindMeshBuffer(meshBuffer);
-}
-
-bool Caches::bindMeshBuffer(const GLuint buffer) {
-    if (mCurrentBuffer != buffer) {
-        glBindBuffer(GL_ARRAY_BUFFER, buffer);
-        mCurrentBuffer = buffer;
-        return true;
-    }
-    return false;
-}
-
-bool Caches::unbindMeshBuffer() {
-    if (mCurrentBuffer) {
-        glBindBuffer(GL_ARRAY_BUFFER, 0);
-        mCurrentBuffer = 0;
-        return true;
-    }
-    return false;
-}
-
-bool Caches::bindIndicesBufferInternal(const GLuint buffer) {
-    if (mCurrentIndicesBuffer != buffer) {
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
-        mCurrentIndicesBuffer = buffer;
-        return true;
-    }
-    return false;
-}
-
-bool Caches::bindQuadIndicesBuffer() {
-    if (!mMeshIndices) {
-        std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[gMaxNumberOfQuads * 6]);
-        for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
-            uint16_t quad = i * 4;
-            int index = i * 6;
-            regionIndices[index    ] = quad;       // top-left
-            regionIndices[index + 1] = quad + 1;   // top-right
-            regionIndices[index + 2] = quad + 2;   // bottom-left
-            regionIndices[index + 3] = quad + 2;   // bottom-left
-            regionIndices[index + 4] = quad + 1;   // top-right
-            regionIndices[index + 5] = quad + 3;   // bottom-right
-        }
-
-        glGenBuffers(1, &mMeshIndices);
-        bool force = bindIndicesBufferInternal(mMeshIndices);
-        glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
-                regionIndices.get(), GL_STATIC_DRAW);
-        return force;
-    }
-
-    return bindIndicesBufferInternal(mMeshIndices);
-}
-
-bool Caches::bindShadowIndicesBuffer() {
-    if (!mShadowStripsIndices) {
-        std::unique_ptr<uint16_t[]> shadowIndices(new uint16_t[MAX_SHADOW_INDEX_COUNT]);
-        ShadowTessellator::generateShadowIndices(shadowIndices.get());
-        glGenBuffers(1, &mShadowStripsIndices);
-        bool force = bindIndicesBufferInternal(mShadowStripsIndices);
-        glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
-            shadowIndices.get(), GL_STATIC_DRAW);
-        return force;
-    }
-
-    return bindIndicesBufferInternal(mShadowStripsIndices);
-}
-
-bool Caches::unbindIndicesBuffer() {
-    if (mCurrentIndicesBuffer) {
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-        mCurrentIndicesBuffer = 0;
-        return true;
-    }
-    return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// PBO
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::bindPixelBuffer(const GLuint buffer) {
-    if (mCurrentPixelBuffer != buffer) {
-        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
-        mCurrentPixelBuffer = buffer;
-        return true;
-    }
-    return false;
-}
-
-bool Caches::unbindPixelBuffer() {
-    if (mCurrentPixelBuffer) {
-        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-        mCurrentPixelBuffer = 0;
-        return true;
-    }
-    return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Meshes and textures
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
-    if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
-        GLuint slot = currentProgram->position;
-        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
-        mCurrentPositionPointer = vertices;
-        mCurrentPositionStride = stride;
-    }
-}
-
-void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
-    if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
-        GLuint slot = currentProgram->texCoords;
-        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
-        mCurrentTexCoordsPointer = vertices;
-        mCurrentTexCoordsStride = stride;
-    }
-}
-
-void Caches::resetVertexPointers() {
-    mCurrentPositionPointer = this;
-    mCurrentTexCoordsPointer = this;
-}
-
-void Caches::resetTexCoordsVertexPointer() {
-    mCurrentTexCoordsPointer = this;
-}
-
-void Caches::enableTexCoordsVertexArray() {
-    if (!mTexCoordsArrayEnabled) {
-        glEnableVertexAttribArray(Program::kBindingTexCoords);
-        mCurrentTexCoordsPointer = this;
-        mTexCoordsArrayEnabled = true;
-    }
-}
-
-void Caches::disableTexCoordsVertexArray() {
-    if (mTexCoordsArrayEnabled) {
-        glDisableVertexAttribArray(Program::kBindingTexCoords);
-        mTexCoordsArrayEnabled = false;
-    }
-}
-
-void Caches::activeTexture(GLuint textureUnit) {
-    if (mTextureUnit != textureUnit) {
-        glActiveTexture(gTextureUnits[textureUnit]);
-        mTextureUnit = textureUnit;
-    }
-}
-
-void Caches::resetActiveTexture() {
-    mTextureUnit = -1;
-}
-
-void Caches::bindTexture(GLuint texture) {
-    if (mBoundTextures[mTextureUnit] != texture) {
-        glBindTexture(GL_TEXTURE_2D, texture);
-        mBoundTextures[mTextureUnit] = texture;
-    }
-}
-
-void Caches::bindTexture(GLenum target, GLuint texture) {
-    if (target == GL_TEXTURE_2D) {
-        bindTexture(texture);
-    } else {
-        // GLConsumer directly calls glBindTexture() with
-        // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
-        // since the cached state could be stale
-        glBindTexture(target, texture);
-    }
-}
-
-void Caches::deleteTexture(GLuint texture) {
-    // When glDeleteTextures() is called on a currently bound texture,
-    // OpenGL ES specifies that the texture is then considered unbound
-    // Consider the following series of calls:
-    //
-    // glGenTextures -> creates texture name 2
-    // glBindTexture(2)
-    // glDeleteTextures(2) -> 2 is now unbound
-    // glGenTextures -> can return 2 again
-    //
-    // If we don't call glBindTexture(2) after the second glGenTextures
-    // call, any texture operation will be performed on the default
-    // texture (name=0)
-
-    unbindTexture(texture);
-
-    glDeleteTextures(1, &texture);
-}
-
-void Caches::resetBoundTextures() {
-    memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
-}
-
-void Caches::unbindTexture(GLuint texture) {
-    for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
-        if (mBoundTextures[i] == texture) {
-            mBoundTextures[i] = 0;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Scissor
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
-    if (scissorEnabled && (x != mScissorX || y != mScissorY ||
-            width != mScissorWidth || height != mScissorHeight)) {
-
-        if (x < 0) {
-            width += x;
-            x = 0;
-        }
-        if (y < 0) {
-            height += y;
-            y = 0;
-        }
-        if (width < 0) {
-            width = 0;
-        }
-        if (height < 0) {
-            height = 0;
-        }
-        glScissor(x, y, width, height);
-
-        mScissorX = x;
-        mScissorY = y;
-        mScissorWidth = width;
-        mScissorHeight = height;
-
-        return true;
-    }
-    return false;
-}
-
-bool Caches::enableScissor() {
-    if (!scissorEnabled) {
-        glEnable(GL_SCISSOR_TEST);
-        scissorEnabled = true;
-        resetScissor();
-        return true;
-    }
-    return false;
-}
-
-bool Caches::disableScissor() {
-    if (scissorEnabled) {
-        glDisable(GL_SCISSOR_TEST);
-        scissorEnabled = false;
-        return true;
-    }
-    return false;
-}
-
-void Caches::setScissorEnabled(bool enabled) {
-    if (scissorEnabled != enabled) {
-        if (enabled) glEnable(GL_SCISSOR_TEST);
-        else glDisable(GL_SCISSOR_TEST);
-        scissorEnabled = enabled;
-    }
-}
-
-void Caches::resetScissor() {
-    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // Tiling
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -682,7 +394,7 @@
 TextureVertex* Caches::getRegionMesh() {
     // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
     if (!mRegionMesh) {
-        mRegionMesh.reset(new TextureVertex[gMaxNumberOfQuads * 4]);
+        mRegionMesh.reset(new TextureVertex[kMaxNumberOfQuads * 4]);
     }
 
     return mRegionMesh.get();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b0eebd7..18bb5e6 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,7 +21,28 @@
     #define LOG_TAG "OpenGLRenderer"
 #endif
 
+
+#include "AssetAtlas.h"
+#include "Dither.h"
+#include "Extensions.h"
+#include "FboCache.h"
+#include "GradientCache.h"
+#include "LayerCache.h"
+#include "PatchCache.h"
+#include "ProgramCache.h"
+#include "PathCache.h"
+#include "RenderBufferCache.h"
+#include "renderstate/PixelBufferState.h"
+#include "renderstate/TextureState.h"
+#include "ResourceCache.h"
+#include "TessellationCache.h"
+#include "TextDropShadowCache.h"
+#include "TextureCache.h"
+#include "thread/TaskProcessor.h"
+#include "thread/TaskManager.h"
+
 #include <vector>
+#include <memory>
 
 #include <GLES3/gl3.h>
 
@@ -33,90 +54,37 @@
 
 #include <SkPath.h>
 
-#include "thread/TaskProcessor.h"
-#include "thread/TaskManager.h"
-
-#include "AssetAtlas.h"
-#include "Extensions.h"
-#include "TextureCache.h"
-#include "LayerCache.h"
-#include "RenderBufferCache.h"
-#include "GradientCache.h"
-#include "PatchCache.h"
-#include "ProgramCache.h"
-#include "PathCache.h"
-#include "TessellationCache.h"
-#include "TextDropShadowCache.h"
-#include "FboCache.h"
-#include "ResourceCache.h"
-#include "Stencil.h"
-#include "Dither.h"
-
 namespace android {
 namespace uirenderer {
 
 class GammaFontRenderer;
 
 ///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
-
-// GL ES 2.0 defines that at least 16 texture units must be supported
-#define REQUIRED_TEXTURE_UNITS_COUNT 3
-
-// Maximum number of quads that pre-allocated meshes can draw
-static const uint32_t gMaxNumberOfQuads = 2048;
-
-// Generates simple and textured vertices
-#define FV(x, y, u, v) { x, y, u, v }
-
-// This array is never used directly but used as a memcpy source in the
-// OpenGLRenderer constructor
-static const TextureVertex gMeshVertices[] = {
-        FV(0.0f, 0.0f, 0.0f, 0.0f),
-        FV(1.0f, 0.0f, 1.0f, 0.0f),
-        FV(0.0f, 1.0f, 0.0f, 1.0f),
-        FV(1.0f, 1.0f, 1.0f, 1.0f)
-};
-static const GLsizei gMeshStride = sizeof(TextureVertex);
-static const GLsizei gVertexStride = sizeof(Vertex);
-static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
-static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
-static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
-static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
-static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
-static const GLsizei gMeshCount = 4;
-
-// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
-static const GLenum gTextureUnits[] = {
-    GL_TEXTURE0,
-    GL_TEXTURE1,
-    GL_TEXTURE2
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Debug
-///////////////////////////////////////////////////////////////////////////////
-
-struct CacheLogger {
-    CacheLogger() {
-        INIT_LOGD("Creating OpenGL renderer caches");
-    }
-}; // struct CacheLogger
-
-///////////////////////////////////////////////////////////////////////////////
 // Caches
 ///////////////////////////////////////////////////////////////////////////////
 
 class RenderNode;
 class RenderState;
 
-class ANDROID_API Caches: public Singleton<Caches> {
-    Caches();
+class ANDROID_API Caches {
+public:
+    static Caches& createInstance(RenderState& renderState) {
+        LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted");
+        sInstance = new Caches(renderState);
+        return *sInstance;
+    }
 
-    friend class Singleton<Caches>;
+    static Caches& getInstance() {
+        LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created");
+        return *sInstance;
+    }
 
-    CacheLogger mLogger;
+    static bool hasInstance() {
+        return sInstance != 0;
+    }
+private:
+    Caches(RenderState& renderState);
+    static Caches* sInstance;
 
 public:
     enum FlushMode {
@@ -135,8 +103,6 @@
      */
     bool initProperties();
 
-    void setRenderState(RenderState* renderState) { mRenderState = renderState; }
-
     /**
      * Flush the cache.
      *
@@ -175,116 +141,6 @@
      */
     void deleteLayerDeferred(Layer* layer);
 
-    /**
-     * Binds the VBO used to render simple textured quads.
-     */
-    bool bindMeshBuffer();
-
-    /**
-     * Binds the specified VBO if needed.
-     */
-    bool bindMeshBuffer(const GLuint buffer);
-
-    /**
-     * Unbinds the VBO used to render simple textured quads.
-     */
-    bool unbindMeshBuffer();
-
-    /**
-     * Binds a global indices buffer that can draw up to
-     * gMaxNumberOfQuads quads.
-     */
-    bool bindQuadIndicesBuffer();
-    bool bindShadowIndicesBuffer();
-    bool unbindIndicesBuffer();
-
-    /**
-     * Binds the specified buffer as the current GL unpack pixel buffer.
-     */
-    bool bindPixelBuffer(const GLuint buffer);
-
-    /**
-     * Resets the current unpack pixel buffer to 0 (default value.)
-     */
-    bool unbindPixelBuffer();
-
-    /**
-     * Binds an attrib to the specified float vertex pointer.
-     * Assumes a stride of gMeshStride and a size of 2.
-     */
-    void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
-
-    /**
-     * Binds an attrib to the specified float vertex pointer.
-     * Assumes a stride of gMeshStride and a size of 2.
-     */
-    void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
-
-    /**
-     * Resets the vertex pointers.
-     */
-    void resetVertexPointers();
-    void resetTexCoordsVertexPointer();
-
-    void enableTexCoordsVertexArray();
-    void disableTexCoordsVertexArray();
-
-    /**
-     * Activate the specified texture unit. The texture unit must
-     * be specified using an integer number (0 for GL_TEXTURE0 etc.)
-     */
-    void activeTexture(GLuint textureUnit);
-
-    /**
-     * Invalidate the cached value of the active texture unit.
-     */
-    void resetActiveTexture();
-
-    /**
-     * Binds the specified texture as a GL_TEXTURE_2D texture.
-     * All texture bindings must be performed with this method or
-     * bindTexture(GLenum, GLuint).
-     */
-    void bindTexture(GLuint texture);
-
-    /**
-     * Binds the specified texture with the specified render target.
-     * All texture bindings must be performed with this method or
-     * bindTexture(GLuint).
-     */
-    void bindTexture(GLenum target, GLuint texture);
-
-    /**
-     * Deletes the specified texture and clears it from the cache
-     * of bound textures.
-     * All textures must be deleted using this method.
-     */
-    void deleteTexture(GLuint texture);
-
-    /**
-     * Signals that the cache of bound textures should be cleared.
-     * Other users of the context may have altered which textures are bound.
-     */
-    void resetBoundTextures();
-
-    /**
-     * Clear the cache of bound textures.
-     */
-    void unbindTexture(GLuint texture);
-
-    /**
-     * Sets the scissor for the current surface.
-     */
-    bool setScissor(GLint x, GLint y, GLint width, GLint height);
-
-    /**
-     * Resets the scissor state.
-     */
-    void resetScissor();
-
-    bool enableScissor();
-    bool disableScissor();
-    void setScissorEnabled(bool enabled);
 
     void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard);
     void endTiling();
@@ -306,18 +162,9 @@
     void registerFunctors(uint32_t functorCount);
     void unregisterFunctors(uint32_t functorCount);
 
-    bool blend;
-    GLenum lastSrcMode;
-    GLenum lastDstMode;
-    Program* currentProgram;
-    bool scissorEnabled;
-
     bool drawDeferDisabled;
     bool drawReorderDisabled;
 
-    // VBO to draw with
-    GLuint meshBuffer;
-
     // Misc
     GLint maxTextureSize;
 
@@ -331,14 +178,18 @@
         kStencilShowRegion
     };
     StencilClipDebug debugStencilClip;
-
+private:
+    // Declared before gradientCache and programCache which need this to initialize.
+    // TODO: cleanup / move elsewhere
+    Extensions mExtensions;
+public:
     TextureCache textureCache;
     LayerCache layerCache;
     RenderBufferCache renderBufferCache;
     GradientCache gradientCache;
-    ProgramCache programCache;
-    PathCache pathCache;
     PatchCache patchCache;
+    PathCache pathCache;
+    ProgramCache programCache;
     TessellationCache tessellationCache;
     TextDropShadowCache dropShadowCache;
     FboCache fboCache;
@@ -348,7 +199,6 @@
     TaskManager tasks;
 
     Dither dither;
-    Stencil stencil;
 
     bool gpuPixelBuffersEnabled;
 
@@ -371,6 +221,14 @@
     int propertyAmbientShadowStrength;
     int propertySpotShadowStrength;
 
+    void setProgram(const ProgramDescription& description);
+    void setProgram(Program* program);
+
+    Extensions& extensions() { return mExtensions; }
+    Program& program() { return *mProgram; }
+    PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
+    TextureState& textureState() { return *mTextureState; }
+
 private:
     enum OverdrawColorSet {
         kColorSet_Default = 0,
@@ -382,8 +240,6 @@
     void initConstraints();
     void initStaticProperties();
 
-    bool bindIndicesBufferInternal(const GLuint buffer);
-
     static void eventMarkNull(GLsizei length, const GLchar* marker) { }
     static void startMarkNull(GLsizei length, const GLchar* marker) { }
     static void endMarkNull() { }
@@ -396,32 +252,11 @@
         if (label) *label = '\0';
     }
 
-    GLuint mCurrentBuffer;
-    GLuint mCurrentIndicesBuffer;
-    GLuint mCurrentPixelBuffer;
-    const void* mCurrentPositionPointer;
-    GLsizei mCurrentPositionStride;
-    const void* mCurrentTexCoordsPointer;
-    GLsizei mCurrentTexCoordsStride;
-
-    bool mTexCoordsArrayEnabled;
-
-    GLuint mTextureUnit;
-
-    GLint mScissorX;
-    GLint mScissorY;
-    GLint mScissorWidth;
-    GLint mScissorHeight;
-
-    Extensions& mExtensions;
+    RenderState* mRenderState;
 
     // Used to render layers
     std::unique_ptr<TextureVertex[]> mRegionMesh;
 
-    // Global index buffer
-    GLuint mMeshIndices;
-    GLuint mShadowStripsIndices;
-
     mutable Mutex mGarbageLock;
     Vector<Layer*> mLayerGarbage;
 
@@ -430,12 +265,13 @@
 
     uint32_t mFunctorsCount;
 
-    // Caches texture bindings for the GL_TEXTURE_2D target
-    GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
-
     OverdrawColorSet mOverdrawDebugColorSet;
 
-    RenderState* mRenderState;
+    // TODO: move below to RenderState
+    PixelBufferState* mPixelBufferState = nullptr;
+    TextureState* mTextureState = nullptr;
+    Program* mProgram = nullptr; // note: object owned by ProgramCache
+
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index f9c8620..7ad0683 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -42,15 +42,15 @@
      */
     static Canvas* create_canvas(SkCanvas* skiaCanvas);
 
-    // TODO: enable HWUI to either create similar canvas wrapper or subclass
-    //       directly from Canvas
-    //static Canvas* create_canvas(uirenderer::Renderer* renderer);
-
-    // TODO: this is a temporary affordance until all necessary logic can be
-    //       moved within this interface! Further, the return value should
-    //       NOT be unref'd and is valid until this canvas is destroyed or a
-    //       new bitmap is set.
-    virtual SkCanvas* getSkCanvas() = 0;
+    /**
+     *  Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
+     *  It is useful for testing and clients (e.g. Picture/Movie) that expect to
+     *  draw their contents into an SkCanvas.
+     *
+     *  Further, the returned SkCanvas should NOT be unref'd and is valid until
+     *  this canvas is destroyed or a new bitmap is set.
+     */
+    virtual SkCanvas* asSkCanvas() = 0;
 
     virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 36c14c4..d128ffe 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -17,13 +17,6 @@
 #ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
 #define ANDROID_HWUI_DISPLAY_OPERATION_H
 
-#include <SkColor.h>
-#include <SkPath.h>
-#include <SkPathOps.h>
-#include <SkXfermode.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
 #include "OpenGLRenderer.h"
 #include "AssetAtlas.h"
 #include "DeferredDisplayList.h"
@@ -31,11 +24,18 @@
 #include "GammaFontRenderer.h"
 #include "Patch.h"
 #include "RenderNode.h"
-#include "RenderState.h"
+#include "renderstate/RenderState.h"
 #include "UvMapper.h"
 #include "utils/LinearAllocator.h"
 #include "utils/PaintUtils.h"
 
+#include <SkColor.h>
+#include <SkPath.h>
+#include <SkPathOps.h>
+#include <SkXfermode.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
 // Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
 #define OP_LOGS(s) OP_LOG("%s", (s))
 #define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index d98d744..23181bc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -59,6 +59,7 @@
     mPathMap.clear();
     DisplayListData* data = mDisplayListData;
     mDisplayListData = nullptr;
+    mSkiaCanvasProxy.reset(nullptr);
     return data;
 }
 
@@ -94,6 +95,15 @@
     mDisplayListData->functors.add(functor);
 }
 
+SkCanvas* DisplayListRenderer::asSkCanvas() {
+    LOG_ALWAYS_FATAL_IF(!mDisplayListData,
+            "attempting to get an SkCanvas when we are not recording!");
+    if (!mSkiaCanvasProxy) {
+        mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
+    }
+    return mSkiaCanvasProxy.get();
+}
+
 int DisplayListRenderer::save(SkCanvas::SaveFlags flags) {
     addStateOp(new (alloc()) SaveOp((int) flags));
     return mState.save((int) flags);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index d2b3ead..bd0b3b7 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -28,6 +28,7 @@
 #include "Canvas.h"
 #include "CanvasState.h"
 #include "DisplayList.h"
+#include "SkiaCanvasProxy.h"
 #include "RenderNode.h"
 #include "Renderer.h"
 #include "ResourceCache.h"
@@ -136,10 +137,8 @@
 // ----------------------------------------------------------------------------
 // android/graphics/Canvas interface
 // ----------------------------------------------------------------------------
-    virtual SkCanvas* getSkCanvas() override {
-        LOG_ALWAYS_FATAL("DisplayListRenderer has no SkCanvas");
-        return nullptr;
-    }
+    virtual SkCanvas* asSkCanvas() override;
+
     virtual void setBitmap(SkBitmap* bitmap, bool copyState) override {
         LOG_ALWAYS_FATAL("DisplayListRenderer is not backed by a bitmap.");
     }
@@ -244,6 +243,7 @@
 private:
 
     CanvasState mState;
+    std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
 
     enum DeferredBarrierType {
         kBarrier_None,
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 12d9389..1ba6511 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,15 +24,16 @@
 // Lifecycle
 ///////////////////////////////////////////////////////////////////////////////
 
-Dither::Dither(): mCaches(nullptr), mInitialized(false), mDitherTexture(0) {
+Dither::Dither(Caches& caches)
+        : mCaches(caches)
+        , mInitialized(false)
+        , mDitherTexture(0) {
 }
 
 void Dither::bindDitherTexture() {
     if (!mInitialized) {
-        bool useFloatTexture = Extensions::getInstance().hasFloatTextures();
-
         glGenTextures(1, &mDitherTexture);
-        mCaches->bindTexture(mDitherTexture);
+        mCaches.textureState().bindTexture(mDitherTexture);
 
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -40,7 +41,7 @@
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 
-        if (useFloatTexture) {
+        if (mCaches.extensions().hasFloatTextures()) {
             // We use a R16F texture, let's remap the alpha channel to the
             // red channel to avoid changing the shader sampling code on GL ES 3.0+
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
@@ -71,13 +72,13 @@
 
         mInitialized = true;
     } else {
-        mCaches->bindTexture(mDitherTexture);
+        mCaches.textureState().bindTexture(mDitherTexture);
     }
 }
 
 void Dither::clear() {
     if (mInitialized) {
-        mCaches->deleteTexture(mDitherTexture);
+        mCaches.textureState().deleteTexture(mDitherTexture);
         mInitialized = false;
     }
 }
@@ -86,15 +87,13 @@
 // Program management
 ///////////////////////////////////////////////////////////////////////////////
 
-void Dither::setupProgram(Program* program, GLuint* textureUnit) {
-    if (!mCaches) mCaches = &Caches::getInstance();
-
+void Dither::setupProgram(Program& program, GLuint* textureUnit) {
     GLuint textureSlot = (*textureUnit)++;
-    mCaches->activeTexture(textureSlot);
+    mCaches.textureState().activateTexture(textureSlot);
 
     bindDitherTexture();
 
-    glUniform1i(program->getUniform("ditherSampler"), textureSlot);
+    glUniform1i(program.getUniform("ditherSampler"), textureSlot);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 092ebf2..b589b80 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -23,6 +23,7 @@
 namespace uirenderer {
 
 class Caches;
+class Extensions;
 class Program;
 
 // Must be a power of two
@@ -36,15 +37,15 @@
  */
 class Dither {
 public:
-    Dither();
+    Dither(Caches& caches);
 
     void clear();
-    void setupProgram(Program* program, GLuint* textureUnit);
+    void setupProgram(Program& program, GLuint* textureUnit);
 
 private:
     void bindDitherTexture();
 
-    Caches* mCaches;
+    Caches& mCaches;
     bool mInitialized;
     GLuint mDitherTexture;
 };
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 8352c3f..c68822b 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -16,18 +16,16 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
+#include "Extensions.h"
+
+#include "Debug.h"
+#include "Properties.h"
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-
+#include <GLES2/gl2ext.h>
 #include <utils/Log.h>
 
-#include "Debug.h"
-#include "Extensions.h"
-#include "Properties.h"
-
 namespace android {
 
 using namespace uirenderer;
@@ -50,7 +48,7 @@
 // Constructors
 ///////////////////////////////////////////////////////////////////////////////
 
-Extensions::Extensions(): Singleton<Extensions>() {
+Extensions::Extensions() {
     // Query GL extensions
     findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
     mHasNPot = hasGlExtension("GL_OES_texture_npot");
@@ -93,9 +91,6 @@
     }
 }
 
-Extensions::~Extensions() {
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Methods
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 25d4c5e..731001a 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -23,6 +23,8 @@
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
 
+#include <GLES2/gl2.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -30,8 +32,10 @@
 // Classes
 ///////////////////////////////////////////////////////////////////////////////
 
-class ANDROID_API Extensions: public Singleton<Extensions> {
+class ANDROID_API Extensions {
 public:
+    Extensions();
+
     inline bool hasNPot() const { return mHasNPot; }
     inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
     inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
@@ -55,13 +59,8 @@
     void dump() const;
 
 private:
-    Extensions();
-    ~Extensions();
-
     void findExtensions(const char* extensions, SortedVector<String8>& list) const;
 
-    friend class Singleton<Extensions>;
-
     SortedVector<String8> mGlExtensionList;
     SortedVector<String8> mEglExtensionList;
 
diff --git a/libs/hwui/Fence.h b/libs/hwui/Fence.h
deleted file mode 100644
index fc29f7ac1..0000000
--- a/libs/hwui/Fence.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_FENCE_H
-#define ANDROID_HWUI_FENCE_H
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Creating a Fence instance inserts a new sync fence in the OpenGL
- * commands stream. The caller can then wait for the fence to be signaled
- * by calling the wait method.
- */
-class Fence {
-public:
-    enum {
-        /**
-         * Default timeout in nano-seconds for wait()
-         */
-        kDefaultTimeout = 1000000000
-    };
-
-    /**
-     * Inserts a new sync fence in the OpenGL commands stream.
-     */
-    Fence() {
-        mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        if (mDisplay != EGL_NO_DISPLAY) {
-            mFence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, nullptr);
-        } else {
-            mFence = EGL_NO_SYNC_KHR;
-        }
-    }
-
-    /**
-     * Destroys the fence. Any caller waiting on the fence will be
-     * signaled immediately.
-     */
-    ~Fence() {
-        if (mFence != EGL_NO_SYNC_KHR) {
-            eglDestroySyncKHR(mDisplay, mFence);
-        }
-    }
-
-    /**
-     * Blocks the calling thread until this fence is signaled, or until
-     * <timeout> nanoseconds have passed.
-     *
-     * Returns true if waiting for the fence was successful, false if
-     * a timeout or an error occurred.
-     */
-    bool wait(EGLTimeKHR timeout = kDefaultTimeout) {
-        EGLint waitStatus = eglClientWaitSyncKHR(mDisplay, mFence,
-                EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout);
-        if (waitStatus == EGL_FALSE) {
-            ALOGW("Failed to wait for the fence %#x", eglGetError());
-        }
-        return waitStatus == EGL_CONDITION_SATISFIED_KHR;
-    }
-
-private:
-    EGLDisplay mDisplay;
-    EGLSyncKHR mFence;
-
-}; // class Fence
-
-/**
- * An AutoFence creates a Fence instance and waits for the fence
- * to be signaled when the AutoFence is destroyed. This is useful
- * to automatically wait for a series of OpenGL commands to be
- * executed. For example:
- *
- * void drawAndWait() {
- *     glDrawElements();
- *     AutoFence fence;
- * }
- */
-class AutoFence {
-public:
-    AutoFence(EGLTimeKHR timeout = Fence::kDefaultTimeout): mTimeout(timeout) {
-    }
-
-    ~AutoFence() {
-        mFence.wait(mTimeout);
-    }
-
-private:
-    EGLTimeKHR mTimeout;
-    Fence mFence;
-
-}; // class AutoFence
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_FENCE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index a0bc7b0..55b2d19 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -14,7 +14,17 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
+#include "FontRenderer.h"
+
+#include "Caches.h"
+#include "Debug.h"
+#include "Extensions.h"
+#include "OpenGLRenderer.h"
+#include "PixelBuffer.h"
+#include "Rect.h"
+#include "renderstate/RenderState.h"
+#include "utils/Blur.h"
+#include "utils/Timing.h"
 
 #include <SkGlyph.h>
 #include <SkUtils.h>
@@ -27,17 +37,6 @@
 #include <RenderScript.h>
 #endif
 
-#include "utils/Blur.h"
-#include "utils/Timing.h"
-
-#include "Caches.h"
-#include "Debug.h"
-#include "Extensions.h"
-#include "FontRenderer.h"
-#include "OpenGLRenderer.h"
-#include "PixelBuffer.h"
-#include "Rect.h"
-
 namespace android {
 namespace uirenderer {
 
@@ -47,9 +46,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 // TextSetupFunctor
 ///////////////////////////////////////////////////////////////////////////////
-status_t TextSetupFunctor::operator ()(int what, void* data) {
-    Data* typedData = reinterpret_cast<Data*>(data);
-    GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA;
+status_t TextSetupFunctor::setup(GLenum glyphFormat) {
 
     renderer->setupDraw();
     renderer->setupDrawTextGamma(paint);
@@ -287,7 +284,7 @@
     uint32_t cacheWidth = cacheTexture->getWidth();
 
     if (!cacheTexture->getPixelBuffer()) {
-        Caches::getInstance().activeTexture(0);
+        Caches::getInstance().textureState().activateTexture(0);
         // Large-glyph texture memory is allocated only as needed
         cacheTexture->allocateTexture();
     }
@@ -397,10 +394,10 @@
 
 CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
         bool allocate) {
-    CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads);
+    CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
 
     if (allocate) {
-        Caches::getInstance().activeTexture(0);
+        Caches::getInstance().textureState().activateTexture(0);
         cacheTexture->allocateTexture();
         cacheTexture->allocateMesh();
     }
@@ -446,8 +443,8 @@
         if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
             if (cacheTexture->getTextureId() != lastTextureId) {
                 lastTextureId = cacheTexture->getTextureId();
-                caches.activeTexture(0);
-                caches.bindTexture(lastTextureId);
+                caches.textureState().activateTexture(0);
+                caches.textureState().bindTexture(lastTextureId);
             }
 
             if (cacheTexture->upload()) {
@@ -473,7 +470,7 @@
     checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
 
     // Unbind any PBO we might have used to update textures
-    caches.unbindPixelBuffer();
+    caches.pixelBufferState().unbind();
 
     // Reset to default unpack row length to avoid affecting texture
     // uploads in other parts of the renderer
@@ -485,44 +482,48 @@
 }
 
 void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
-    Caches& caches = Caches::getInstance();
+    if (!mFunctor) return;
+
+    Caches& caches = mFunctor->renderer->getCaches();
+    RenderState& renderState = mFunctor->renderer->renderState();
+
     bool first = true;
-    bool force = false;
+    bool forceRebind = false;
     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
         CacheTexture* texture = cacheTextures[i];
         if (texture->canDraw()) {
             if (first) {
                 if (mFunctor) {
-                    TextSetupFunctor::Data functorData(texture->getFormat());
-                    (*mFunctor)(0, &functorData);
+                    mFunctor->setup(texture->getFormat());
                 }
 
                 checkTextureUpdate();
-                caches.bindQuadIndicesBuffer();
+                renderState.meshState().bindQuadIndicesBuffer();
 
                 if (!mDrawn) {
                     // If returns true, a VBO was bound and we must
                     // rebind our vertex attrib pointers even if
                     // they have the same values as the current pointers
-                    force = caches.unbindMeshBuffer();
+                    forceRebind = renderState.meshState().unbindMeshBuffer();
                 }
 
-                caches.activeTexture(0);
+                caches.textureState().activateTexture(0);
                 first = false;
             }
 
-            caches.bindTexture(texture->getTextureId());
+            caches.textureState().bindTexture(texture->getTextureId());
             texture->setLinearFiltering(mLinearFiltering, false);
 
             TextureVertex* mesh = texture->mesh();
-            caches.bindPositionVertexPointer(force, &mesh[0].x);
-            caches.bindTexCoordsVertexPointer(force, &mesh[0].u);
-            force = false;
+            MeshState& meshState = renderState.meshState();
+            meshState.bindPositionVertexPointer(forceRebind, &mesh[0].x);
+            meshState.bindTexCoordsVertexPointer(forceRebind, &mesh[0].u);
 
             glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
                     GL_UNSIGNED_SHORT, texture->indices());
 
             texture->resetMesh();
+            forceRebind = false;
         }
     }
 }
@@ -647,7 +648,7 @@
                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
 
         // Unbind any PBO we might have used
-        Caches::getInstance().unbindPixelBuffer();
+        Caches::getInstance().pixelBufferState().unbind();
 
         blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
     }
@@ -661,7 +662,7 @@
     return image;
 }
 
-void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
+void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextSetupFunctor* functor) {
     checkInit();
 
     mDrawn = false;
@@ -689,7 +690,7 @@
 
 bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
         uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
-        const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
+        const float* positions, Rect* bounds, TextSetupFunctor* functor, bool forceFinish) {
     if (!mCurrentFont) {
         ALOGE("No font set");
         return false;
@@ -707,7 +708,7 @@
 
 bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
         uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
-        float hOffset, float vOffset, Rect* bounds, Functor* functor) {
+        float hOffset, float vOffset, Rect* bounds, TextSetupFunctor* functor) {
     if (!mCurrentFont) {
         ALOGE("No font set");
         return false;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 668ee64..cb63684 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,7 +17,12 @@
 #ifndef ANDROID_HWUI_FONT_RENDERER_H
 #define ANDROID_HWUI_FONT_RENDERER_H
 
-#include <utils/Functor.h>
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
+#include "utils/SortedList.h"
+
 #include <utils/LruCache.h>
 #include <utils/Vector.h>
 #include <utils/StrongPointer.h>
@@ -26,12 +31,6 @@
 
 #include <GLES2/gl2.h>
 
-#include "font/FontUtil.h"
-#include "font/CacheTexture.h"
-#include "font/CachedGlyphInfo.h"
-#include "font/Font.h"
-#include "utils/SortedList.h"
-
 #ifdef ANDROID_ENABLE_RENDERSCRIPT
 #include "RenderScript.h"
 namespace RSC {
@@ -47,26 +46,20 @@
 
 class OpenGLRenderer;
 
-///////////////////////////////////////////////////////////////////////////////
-// TextSetupFunctor
-///////////////////////////////////////////////////////////////////////////////
-class TextSetupFunctor: public Functor {
+class TextSetupFunctor {
 public:
-    struct Data {
-        Data(GLenum glyphFormat) : glyphFormat(glyphFormat) {
-        }
-
-        GLenum glyphFormat;
-    };
-
     TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
-            int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(),
-            renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
-            alpha(alpha), mode(mode), paint(paint) {
+            int alpha, SkXfermode::Mode mode, const SkPaint* paint)
+        : renderer(renderer)
+        , x(x)
+        , y(y)
+        , pureTranslate(pureTranslate)
+        , alpha(alpha)
+        , mode(mode)
+        , paint(paint) {
     }
-    ~TextSetupFunctor() { }
 
-    status_t operator ()(int what, void* data) override;
+    status_t setup(GLenum glyphFormat);
 
     OpenGLRenderer* renderer;
     float x;
@@ -77,10 +70,6 @@
     const SkPaint* paint;
 };
 
-///////////////////////////////////////////////////////////////////////////////
-// FontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
 class FontRenderer {
 public:
     FontRenderer();
@@ -101,22 +90,14 @@
     // bounds is an out parameter
     bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
             uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions,
-            Rect* bounds, Functor* functor, bool forceFinish = true);
+            Rect* bounds, TextSetupFunctor* functor, bool forceFinish = true);
 
     // bounds is an out parameter
     bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
             uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
-            float hOffset, float vOffset, Rect* bounds, Functor* functor);
+            float hOffset, float vOffset, Rect* bounds, TextSetupFunctor* functor);
 
     struct DropShadow {
-        DropShadow() { };
-
-        DropShadow(const DropShadow& dropShadow):
-            width(dropShadow.width), height(dropShadow.height),
-            image(dropShadow.image), penX(dropShadow.penX),
-            penY(dropShadow.penY) {
-        }
-
         uint32_t width;
         uint32_t height;
         uint8_t* image;
@@ -152,7 +133,7 @@
     void flushAllAndInvalidate();
 
     void checkInit();
-    void initRender(const Rect* clip, Rect* bounds, Functor* functor);
+    void initRender(const Rect* clip, Rect* bounds, TextSetupFunctor* functor);
     void finishRender();
 
     void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
@@ -193,7 +174,7 @@
 
     bool mUploadTexture;
 
-    Functor* mFunctor;
+    TextSetupFunctor* mFunctor;
     const Rect* mClip;
     Rect* mBounds;
     bool mDrawn;
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index e97a477..0bcd83a 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
-
 #include "Debug.h"
 #include "GammaFontRenderer.h"
 #include "Properties.h"
@@ -96,7 +94,8 @@
 // Shader-based renderer
 ///////////////////////////////////////////////////////////////////////////////
 
-ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma): GammaFontRenderer() {
+ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
+        : GammaFontRenderer() {
     INIT_LOGD("Creating shader gamma font renderer");
     mRenderer = nullptr;
     mMultiGamma = multiGamma;
@@ -123,9 +122,9 @@
 }
 
 void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
-        Program* program) const {
+        Program& program) const {
     if (description.hasGammaCorrection) {
-        glUniform1f(program->getUniform("gamma"), description.gamma);
+        glUniform1f(program.getUniform("gamma"), description.gamma);
     }
 }
 
@@ -139,7 +138,8 @@
 // Lookup-based renderer
 ///////////////////////////////////////////////////////////////////////////////
 
-LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
+LookupGammaFontRenderer::LookupGammaFontRenderer()
+        : GammaFontRenderer() {
     INIT_LOGD("Creating lookup gamma font renderer");
 
     // Compute the gamma tables
@@ -162,7 +162,8 @@
 // Lookup-based renderer, using 3 different correction tables
 ///////////////////////////////////////////////////////////////////////////////
 
-Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() {
+Lookup3GammaFontRenderer::Lookup3GammaFontRenderer()
+        : GammaFontRenderer() {
     INIT_LOGD("Creating lookup3 gamma font renderer");
 
     // Compute the gamma tables
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 19352d7..ca55bf1 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -38,7 +38,7 @@
     virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
 
     virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
-    virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
+    virtual void setupProgram(ProgramDescription& description, Program& program) const = 0;
 
     virtual void endPrecaching() = 0;
 
@@ -86,7 +86,7 @@
     }
 
     void describe(ProgramDescription& description, const SkPaint* paint) const override;
-    void setupProgram(ProgramDescription& description, Program* program) const override;
+    void setupProgram(ProgramDescription& description, Program& program) const override;
 
     void endPrecaching() override;
 
@@ -135,7 +135,7 @@
     void describe(ProgramDescription& description, const SkPaint* paint) const override {
     }
 
-    void setupProgram(ProgramDescription& description, Program* program) const override {
+    void setupProgram(ProgramDescription& description, Program& program) const override {
     }
 
     void endPrecaching() override;
@@ -171,7 +171,7 @@
     void describe(ProgramDescription& description, const SkPaint* paint) const override {
     }
 
-    void setupProgram(ProgramDescription& description, Program* program) const override {
+    void setupProgram(ProgramDescription& description, Program& program) const override {
     }
 
     void endPrecaching() override;
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
new file mode 100644
index 0000000..9150869
--- /dev/null
+++ b/libs/hwui/Glop.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_GLOP_H
+#define ANDROID_HWUI_GLOP_H
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "utils/Macros.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+
+namespace android {
+namespace uirenderer {
+
+class Program;
+
+/*
+ * Enumerates optional vertex attributes
+ *
+ * Position is always enabled by MeshState, these other attributes
+ * are enabled/disabled dynamically based on mesh content.
+ */
+enum VertexAttribFlags {
+    // NOTE: position attribute always enabled
+    kNone_Attrib = 0,
+    kTextureCoord_Attrib = 1 << 0,
+    kColor_Attrib = 1 << 1,
+    kAlpha_Attrib = 1 << 2,
+};
+
+
+/**
+ * Structure containing all data required to issue a single OpenGL draw
+ *
+ * Includes all of the mesh, fill, and GL state required to perform
+ * the operation. Pieces of data are either directly copied into the
+ * structure, or stored as a pointer or GL object reference to data
+ * managed
+ */
+// TODO: PREVENT_COPY_AND_ASSIGN(...) or similar
+struct Glop {
+    struct FloatColor {
+        float a, r, g, b;
+    };
+
+    Rect bounds;
+
+    /*
+     * Stores mesh - vertex and index data.
+     *
+     * buffer objects and void*s are mutually exclusive
+     * indices are optional
+     */
+    struct Mesh {
+        VertexAttribFlags vertexFlags;
+        GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
+        GLuint vertexBufferObject;
+        GLuint indexBufferObject;
+        const void* vertices;
+        const void* indices;
+        int vertexCount;
+        GLsizei stride;
+    } mesh;
+
+    struct Fill {
+        Program* program;
+        FloatColor color;
+
+        /* TODO
+        union shader {
+            //...
+        }; TODO
+        */
+        ProgramDescription::ColorFilterMode filterMode;
+        union Filter {
+            struct Matrix {
+                float matrix[16];
+                float vector[4];
+            } matrix;
+            FloatColor color;
+        } filter;
+    } fill;
+
+    struct Transform {
+        Matrix4 ortho; // TODO: out of op, since this is static per FBO
+        Matrix4 modelView;
+        Matrix4 canvas;
+        bool fudgingOffset;
+    } transform;
+
+    struct Blend {
+        GLenum src;
+        GLenum dst;
+    } blend;
+
+    /**
+     * Additional render state to enumerate:
+     * - scissor + (bits for whether each of LTRB needed?)
+     * - stencil mode (draw into, mask, count, etc)
+     */
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // ANDROID_HWUI_GLOP_H
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
new file mode 100644
index 0000000..e22af40
--- /dev/null
+++ b/libs/hwui/GlopBuilder.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+#include "GlopBuilder.h"
+
+#include "Caches.h"
+#include "Glop.h"
+#include "Matrix.h"
+#include "renderstate/MeshState.h"
+#include "renderstate/RenderState.h"
+#include "SkiaShader.h"
+#include "Texture.h"
+#include "utils/PaintUtils.h"
+#include "VertexBuffer.h"
+
+#include <GLES2/gl2.h>
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+#define TRIGGER_STAGE(stageFlag) \
+    LOG_ALWAYS_FATAL_IF(stageFlag & mStageFlags, "Stage %d cannot be run twice"); \
+    mStageFlags = static_cast<StageFlags>(mStageFlags | stageFlag)
+
+GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop)
+        : mRenderState(renderState)
+        , mCaches(caches)
+        , mOutGlop(outGlop){
+    mStageFlags = kInitialStage;
+}
+
+GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) {
+    TRIGGER_STAGE(kMeshStage);
+
+    const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
+
+    bool alphaVertex = flags & VertexBuffer::kAlpha;
+    bool indices = flags & VertexBuffer::kIndices;
+    mOutGlop->mesh.vertexFlags = alphaVertex ? kAlpha_Attrib : kNone_Attrib;
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+    mOutGlop->mesh.vertexBufferObject = 0;
+    mOutGlop->mesh.vertices = vertexBuffer.getBuffer();
+    mOutGlop->mesh.indexBufferObject = 0;
+    mOutGlop->mesh.indices = vertexBuffer.getIndices();
+    mOutGlop->mesh.vertexCount = indices
+            ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
+    mOutGlop->mesh.stride = alphaVertex ? kAlphaVertexStride : kVertexStride;
+
+    mDescription.hasVertexAlpha = alphaVertex;
+    mDescription.useShadowAlphaInterp = shadowInterp;
+    return *this;
+}
+
+GlopBuilder& GlopBuilder::setMeshUnitQuad() {
+    TRIGGER_STAGE(kMeshStage);
+
+    mOutGlop->mesh.vertexFlags = kNone_Attrib;
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+    mOutGlop->mesh.vertexBufferObject = mRenderState.meshState().getUnitQuadVBO();
+    mOutGlop->mesh.vertices = nullptr;
+    mOutGlop->mesh.indexBufferObject = 0;
+    mOutGlop->mesh.indices = nullptr;
+    mOutGlop->mesh.vertexCount = 4;
+    mOutGlop->mesh.stride = kTextureVertexStride;
+    return *this;
+}
+
+GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho,
+        const Matrix4& transform, bool fudgingOffset) {
+    TRIGGER_STAGE(kTransformStage);
+
+    mOutGlop->transform.ortho.load(ortho);
+    mOutGlop->transform.canvas.load(transform);
+    mOutGlop->transform.fudgingOffset = fudgingOffset;
+    return *this;
+}
+
+GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
+    TRIGGER_STAGE(kModelViewStage);
+    mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
+    mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+    mOutGlop->bounds = destination;
+    return *this;
+}
+
+GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
+    TRIGGER_STAGE(kModelViewStage);
+    mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+    mOutGlop->bounds = source;
+    mOutGlop->bounds.translate(offsetX, offsetY);
+    return *this;
+}
+
+GlopBuilder& GlopBuilder::setPaint(const SkPaint* paint, float alphaScale) {
+    TRIGGER_STAGE(kFillStage);
+
+    // TODO: support null paint
+    const SkShader* shader = paint->getShader();
+    const SkColorFilter* colorFilter = paint->getColorFilter();
+
+    SkXfermode::Mode mode = PaintUtils::getXfermode(paint->getXfermode());
+    if (mode != SkXfermode::kClear_Mode) {
+        int color = paint->getColor();
+        float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
+        if (!shader) {
+            float colorScale = alpha / 255.0f;
+            mOutGlop->fill.color = {
+                    alpha,
+                    colorScale * SkColorGetR(color),
+                    colorScale * SkColorGetG(color),
+                    colorScale * SkColorGetB(color)
+            };
+        } else {
+            mOutGlop->fill.color = { alpha, 1, 1, 1 };
+        }
+    } else {
+        mOutGlop->fill.color = { 1, 0, 0, 0 };
+    }
+    const bool SWAP_SRC_DST = false;
+
+    mOutGlop->blend = { GL_ZERO, GL_ZERO };
+    if (mOutGlop->fill.color.a < 1.0f
+            || PaintUtils::isBlendedShader(shader)
+            || PaintUtils::isBlendedColorFilter(colorFilter)
+            || mode != SkXfermode::kSrcOver_Mode) {
+        if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) {
+            Blend::getFactors(mode, SWAP_SRC_DST,
+                    &mOutGlop->blend.src, &mOutGlop->blend.dst);
+        } else {
+            // These blend modes are not supported by OpenGL directly and have
+            // to be implemented using shaders. Since the shader will perform
+            // the blending, don't enable GL blending off here
+            // If the blend mode cannot be implemented using shaders, fall
+            // back to the default SrcOver blend mode instead
+            if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
+                mDescription.framebufferMode = mode;
+                mDescription.swapSrcDst = SWAP_SRC_DST;
+                // blending in shader, don't enable
+            } else {
+                // unsupported
+                Blend::getFactors(SkXfermode::kSrcOver_Mode, SWAP_SRC_DST,
+                        &mOutGlop->blend.src, &mOutGlop->blend.dst);
+            }
+        }
+    }
+
+    if (shader) {
+        SkiaShader::describe(&mCaches, mDescription, mCaches.extensions(), *shader);
+        // TODO: store shader data
+        LOG_ALWAYS_FATAL("shaders not yet supported");
+    }
+
+    if (colorFilter) {
+        SkColor color;
+        SkXfermode::Mode mode;
+        SkScalar srcColorMatrix[20];
+        if (colorFilter->asColorMode(&color, &mode)) {
+            mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend;
+            mDescription.colorMode = mode;
+
+            const float alpha = SkColorGetA(color) / 255.0f;
+            float colorScale = alpha / 255.0f;
+            mOutGlop->fill.filter.color = {
+                    alpha,
+                    colorScale * SkColorGetR(color),
+                    colorScale * SkColorGetG(color),
+                    colorScale * SkColorGetB(color),
+            };
+        } else if (colorFilter->asColorMatrix(srcColorMatrix)) {
+            mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix;
+
+            float* colorMatrix = mOutGlop->fill.filter.matrix.matrix;
+            memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
+            memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
+            memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
+            memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
+
+            // Skia uses the range [0..255] for the addition vector, but we need
+            // the [0..1] range to apply the vector in GLSL
+            float* colorVector = mOutGlop->fill.filter.matrix.vector;
+            colorVector[0] = srcColorMatrix[4] / 255.0f;
+            colorVector[1] = srcColorMatrix[9] / 255.0f;
+            colorVector[2] = srcColorMatrix[14] / 255.0f;
+            colorVector[3] = srcColorMatrix[19] / 255.0f;
+        }
+    } else {
+        mOutGlop->fill.filterMode = ProgramDescription::kColorNone;
+    }
+
+    return *this;
+}
+
+void GlopBuilder::build() {
+    LOG_ALWAYS_FATAL_IF(mStageFlags != kAllStages, "glop not fully prepared!");
+
+    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+    mOutGlop->fill.program = mCaches.programCache.get(mDescription);
+    mOutGlop->transform.canvas.mapRect(mOutGlop->bounds);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
new file mode 100644
index 0000000..c7464cd
--- /dev/null
+++ b/libs/hwui/GlopBuilder.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_GLOPBUILDER_H
+#define RENDERSTATE_GLOPBUILDER_H
+
+#include "OpenGLRenderer.h"
+#include "Program.h"
+#include "utils/Macros.h"
+
+class SkPaint;
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+struct Glop;
+class Matrix4;
+class RenderState;
+class Texture;
+class VertexBuffer;
+
+class GlopBuilder {
+    PREVENT_COPY_AND_ASSIGN(GlopBuilder);
+public:
+    GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
+    GlopBuilder& setMeshUnitQuad();
+    GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp);
+
+    GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset);
+
+    GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
+    GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source);
+
+    GlopBuilder& setPaint(const SkPaint* paint, float alphaScale);
+    void build();
+private:
+    enum StageFlags {
+        kInitialStage = 0,
+        kMeshStage = 1 << 0,
+        kTransformStage = 1 << 1,
+        kModelViewStage = 1 << 2,
+        kFillStage = 1 << 3,
+        kAllStages = kMeshStage | kTransformStage | kModelViewStage | kFillStage,
+    } mStageFlags;
+
+    ProgramDescription mDescription;
+    RenderState& mRenderState;
+    Caches& mCaches;
+    Glop* mOutGlop;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_GLOPBUILDER_H
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 0987d9b..fb4c785 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -62,9 +62,12 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-GradientCache::GradientCache():
-        mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
-        mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
+GradientCache::GradientCache(Extensions& extensions)
+        : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity)
+        , mSize(0)
+        , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE))
+        , mUseFloatTexture(extensions.hasFloatTextures())
+        , mHasNpot(extensions.hasNPot()){
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) {
         INIT_LOGD("  Setting gradient cache size to %sMB", property);
@@ -76,16 +79,6 @@
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
 
     mCache.setOnEntryRemovedListener(this);
-
-    const Extensions& extensions = Extensions::getInstance();
-    mUseFloatTexture = extensions.hasFloatTextures();
-    mHasNpot = extensions.hasNPot();
-}
-
-GradientCache::GradientCache(uint32_t maxByteSize):
-        mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
-        mSize(0), mMaxSize(maxByteSize) {
-    mCache.setOnEntryRemovedListener(this);
 }
 
 GradientCache::~GradientCache() {
@@ -285,7 +278,7 @@
     memcpy(pixels + rowBytes, pixels, rowBytes);
 
     glGenTextures(1, &texture->id);
-    Caches::getInstance().bindTexture(texture->id);
+    Caches::getInstance().textureState().bindTexture(texture->id);
     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
     if (mUseFloatTexture) {
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 9176c76..08319ea 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_HWUI_GRADIENT_CACHE_H
 #define ANDROID_HWUI_GRADIENT_CACHE_H
 
+#include <memory>
+
 #include <GLES3/gl3.h>
 
 #include <SkShader.h>
@@ -102,8 +104,7 @@
  */
 class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> {
 public:
-    GradientCache();
-    GradientCache(uint32_t maxByteSize);
+    GradientCache(Extensions& extensions);
     ~GradientCache();
 
     /**
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
index edf3930..a31c546 100644
--- a/libs/hwui/Image.cpp
+++ b/libs/hwui/Image.cpp
@@ -39,7 +39,7 @@
     } else {
         // Create a 2D texture to sample from the EGLImage
         glGenTextures(1, &mTexture);
-        Caches::getInstance().bindTexture(mTexture);
+        Caches::getInstance().textureState().bindTexture(mTexture);
         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
 
         GLenum status = GL_NO_ERROR;
@@ -54,7 +54,7 @@
         eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
         mImage = EGL_NO_IMAGE_KHR;
 
-        Caches::getInstance().deleteTexture(mTexture);
+        Caches::getInstance().textureState().deleteTexture(mTexture);
         mTexture = 0;
     }
 }
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 7388e3c..7a026ef 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -16,17 +16,18 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <utils/Log.h>
+#include "Layer.h"
 
 #include "Caches.h"
 #include "DeferredDisplayList.h"
-#include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "RenderNode.h"
-#include "RenderState.h"
+#include "renderstate/RenderState.h"
 #include "utils/TraceUtils.h"
 
+#include <utils/Log.h>
+
 #define ATRACE_LAYER_WORK(label) \
     ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
             label, \
@@ -67,15 +68,23 @@
 }
 
 Layer::~Layer() {
-    renderState.requireGLContext();
     renderState.unregisterLayer(this);
     SkSafeUnref(colorFilter);
-    removeFbo();
-    deleteTexture();
+
+    if (stencil || fbo || texture.id) {
+        renderState.requireGLContext();
+        removeFbo();
+        deleteTexture();
+    }
 
     delete[] mesh;
 }
 
+void Layer::onGlContextLost() {
+    removeFbo();
+    deleteTexture();
+}
+
 uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
     return uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
 }
@@ -125,7 +134,7 @@
     setSize(desiredWidth, desiredHeight);
 
     if (fbo) {
-        caches.activeTexture(0);
+        caches.textureState().activateTexture(0);
         bindTexture();
         allocateTexture();
 
@@ -186,7 +195,7 @@
 
 void Layer::bindTexture() const {
     if (texture.id) {
-        caches.bindTexture(renderTarget, texture.id);
+        caches.textureState().bindTexture(renderTarget, texture.id);
     }
 }
 
@@ -210,7 +219,7 @@
 }
 
 void Layer::clearTexture() {
-    caches.unbindTexture(texture.id);
+    caches.textureState().unbindTexture(texture.id);
     texture.id = 0;
 }
 
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 3b4f293..84ff021 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -288,6 +288,12 @@
     void postDecStrong();
 
     /**
+     * Lost the GL context but the layer is still around, mark it invalid internally
+     * so the dtor knows not to do any GL work
+     */
+    void onGlContextLost();
+
+    /**
      * Bounds of the layer.
      */
     Rect layer;
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 6ad1b197..b4b14e8 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -17,18 +17,19 @@
 #define LOG_TAG "OpenGLRenderer"
 #define ATRACE_TAG ATRACE_TAG_VIEW
 
-#include <ui/Rect.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include "RenderState.h"
 #include "LayerCache.h"
 #include "LayerRenderer.h"
 #include "Matrix.h"
 #include "Properties.h"
 #include "Rect.h"
+#include "renderstate/RenderState.h"
 #include "utils/TraceUtils.h"
 
+#include <ui/Rect.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+
 namespace android {
 namespace uirenderer {
 
@@ -48,7 +49,7 @@
         bool opaque) {
     LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
 
-    renderState().bindFramebuffer(mLayer->getFbo());
+    mRenderState.bindFramebuffer(mLayer->getFbo());
 
     const float width = mLayer->layer.getWidth();
     const float height = mLayer->layer.getHeight();
@@ -70,10 +71,10 @@
 
 void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
     if (mLayer->isDirty()) {
-        getCaches().disableScissor();
+        mRenderState.scissor().setEnabled(false);
         glClear(GL_COLOR_BUFFER_BIT);
 
-        getCaches().resetScissor();
+        mRenderState.scissor().reset();
         mLayer->setDirty(false);
     } else {
         OpenGLRenderer::clear(left, top, right, bottom, opaque);
@@ -195,7 +196,7 @@
         return nullptr;
     }
 
-    caches.activeTexture(0);
+    caches.textureState().activateTexture(0);
     Layer* layer = caches.layerCache.get(renderState, width, height);
     if (!layer) {
         ALOGW("Could not obtain a layer");
@@ -282,7 +283,7 @@
     layer->region.clear();
     layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
 
-    Caches::getInstance().activeTexture(0);
+    Caches::getInstance().textureState().activateTexture(0);
     layer->generateTexture();
 
     return layer;
@@ -336,7 +337,7 @@
     if (fbo) {
         // If possible, discard any enqueud operations on deferred
         // rendering architectures
-        if (Extensions::getInstance().hasDiscardFramebuffer()) {
+        if (Caches::getInstance().extensions().hasDiscardFramebuffer()) {
             GLuint previousFbo = renderState.getFramebuffer();
             if (fbo != previousFbo) {
                 renderState.bindFramebuffer(fbo);
@@ -411,8 +412,8 @@
         glGenTextures(1, &texture);
         if ((error = glGetError()) != GL_NO_ERROR) goto error;
 
-        caches.activeTexture(0);
-        caches.bindTexture(texture);
+        caches.textureState().activateTexture(0);
+        caches.textureState().bindTexture(texture);
 
         glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
 
@@ -436,7 +437,7 @@
             renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
                     bitmap->width(), bitmap->height(), !layer->isBlend());
 
-            caches.disableScissor();
+            renderState.scissor().setEnabled(false);
             renderer.translate(0.0f, bitmap->height());
             renderer.scale(1.0f, -1.0f);
 
@@ -474,7 +475,7 @@
         renderState.bindFramebuffer(previousFbo);
         layer->setAlpha(alpha, mode);
         layer->setFbo(previousLayerFbo);
-        caches.deleteTexture(texture);
+        caches.textureState().deleteTexture(texture);
         caches.fboCache.put(fbo);
         renderState.setViewport(previousViewportWidth, previousViewportHeight);
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 82f6ddd..5a0cc1e 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -14,7 +14,26 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
+#include "OpenGLRenderer.h"
+
+#include "DeferredDisplayList.h"
+#include "DisplayListRenderer.h"
+#include "GammaFontRenderer.h"
+#include "Glop.h"
+#include "GlopBuilder.h"
+#include "Patch.h"
+#include "PathTessellator.h"
+#include "Properties.h"
+#include "RenderNode.h"
+#include "renderstate/MeshState.h"
+#include "renderstate/RenderState.h"
+#include "ShadowTessellator.h"
+#include "SkiaShader.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+#include "utils/GLUtils.h"
+#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
 
 #include <stdlib.h>
 #include <stdint.h>
@@ -32,24 +51,6 @@
 
 #include <ui/Rect.h>
 
-#include "OpenGLRenderer.h"
-#include "DeferredDisplayList.h"
-#include "DisplayListRenderer.h"
-#include "Fence.h"
-#include "GammaFontRenderer.h"
-#include "Patch.h"
-#include "PathTessellator.h"
-#include "Properties.h"
-#include "RenderNode.h"
-#include "RenderState.h"
-#include "ShadowTessellator.h"
-#include "SkiaShader.h"
-#include "Vector.h"
-#include "VertexBuffer.h"
-#include "utils/GLUtils.h"
-#include "utils/PaintUtils.h"
-#include "utils/TraceUtils.h"
-
 #if DEBUG_DETAILED_EVENTS
     #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
 #else
@@ -70,55 +71,6 @@
 // Globals
 ///////////////////////////////////////////////////////////////////////////////
 
-/**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
-struct Blender {
-    SkXfermode::Mode mode;
-    GLenum src;
-    GLenum dst;
-}; // struct Blender
-
-// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
-static const Blender gBlends[] = {
-    { SkXfermode::kClear_Mode,    GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kSrc_Mode,      GL_ONE,                 GL_ZERO },
-    { SkXfermode::kDst_Mode,      GL_ZERO,                GL_ONE },
-    { SkXfermode::kSrcOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kDstOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
-    { SkXfermode::kSrcIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
-    { SkXfermode::kDstIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
-    { SkXfermode::kSrcOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
-    { SkXfermode::kDstOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kSrcATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kDstATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
-    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
-    { SkXfermode::kModulate_Mode, GL_ZERO,                GL_SRC_COLOR },
-    { SkXfermode::kScreen_Mode,   GL_ONE,                 GL_ONE_MINUS_SRC_COLOR }
-};
-
-// This array contains the swapped version of each SkXfermode. For instance
-// this array's SrcOver blending mode is actually DstOver. You can refer to
-// createLayer() for more information on the purpose of this array.
-static const Blender gBlendsSwap[] = {
-    { SkXfermode::kClear_Mode,    GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
-    { SkXfermode::kSrc_Mode,      GL_ZERO,                GL_ONE },
-    { SkXfermode::kDst_Mode,      GL_ONE,                 GL_ZERO },
-    { SkXfermode::kSrcOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
-    { SkXfermode::kDstOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kSrcIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
-    { SkXfermode::kDstIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
-    { SkXfermode::kSrcOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kDstOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
-    { SkXfermode::kSrcATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
-    { SkXfermode::kDstATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
-    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
-    { SkXfermode::kModulate_Mode, GL_DST_COLOR,           GL_ZERO },
-    { SkXfermode::kScreen_Mode,   GL_ONE_MINUS_DST_COLOR, GL_ONE }
-};
 
 ///////////////////////////////////////////////////////////////////////////////
 // Functions
@@ -135,10 +87,9 @@
 
 OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
         : mState(*this)
-        , mFrameStarted(false)
         , mCaches(Caches::getInstance())
-        , mExtensions(Extensions::getInstance())
         , mRenderState(renderState)
+        , mFrameStarted(false)
         , mScissorOptimizationDisabled(false)
         , mSuppressTiling(false)
         , mFirstFrameAfterResize(true)
@@ -151,7 +102,7 @@
     memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
     mDrawModifiers.mOverrideLayerAlpha = 1.0f;
 
-    memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
+    memcpy(mMeshVertices, kUnitQuadVertices, sizeof(kUnitQuadVertices));
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -185,8 +136,6 @@
 void OpenGLRenderer::onViewportInitialized() {
     glDisable(GL_DITHER);
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-    glEnableVertexAttribArray(Program::kBindingPosition);
     mFirstFrameAfterResize = true;
 }
 
@@ -233,7 +182,7 @@
     // for each layer and wait until the first drawing command
     // to start the frame
     if (currentSnapshot()->fbo == 0) {
-        syncState();
+        mRenderState.blend().syncEnabled();
         updateLayers();
     } else {
         startFrame();
@@ -244,7 +193,7 @@
     // If we know that we are going to redraw the entire framebuffer,
     // perform a discard to let the driver know we don't need to preserve
     // the back buffer for this frame.
-    if (mExtensions.hasDiscardFramebuffer() &&
+    if (mCaches.extensions().hasDiscardFramebuffer() &&
             left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) {
         const bool isFbo = onGetTargetFbo() == 0;
         const GLenum attachments[] = {
@@ -256,22 +205,14 @@
 
 void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
     if (!opaque) {
-        mCaches.enableScissor();
-        mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
+        mRenderState.scissor().setEnabled(true);
+        mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top);
         glClear(GL_COLOR_BUFFER_BIT);
         mDirty = true;
         return;
     }
 
-    mCaches.resetScissor();
-}
-
-void OpenGLRenderer::syncState() {
-    if (mCaches.blend) {
-        glEnable(GL_BLEND);
-    } else {
-        glDisable(GL_BLEND);
-    }
+    mRenderState.scissor().reset();
 }
 
 void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
@@ -347,7 +288,7 @@
     mRenderState.bindFramebuffer(currentSnapshot()->fbo);
     debugOverdraw(true, false);
 
-    mCaches.resetScissor();
+    mRenderState.scissor().reset();
     dirtyClip();
 }
 
@@ -378,7 +319,7 @@
     if (mState.getDirtyClip()) {
         setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
     }
-    if (mCaches.enableScissor() || prevDirtyClip) {
+    if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) {
         setScissorFromClip();
     }
 
@@ -428,27 +369,29 @@
     if (mCaches.debugOverdraw && onGetTargetFbo() == 0) {
         const Rect* clip = &mTilingClip;
 
-        mCaches.enableScissor();
-        mCaches.setScissor(clip->left, mState.firstSnapshot()->getViewportHeight() - clip->bottom,
-                clip->right - clip->left, clip->bottom - clip->top);
+        mRenderState.scissor().setEnabled(true);
+        mRenderState.scissor().set(clip->left,
+                mState.firstSnapshot()->getViewportHeight() - clip->bottom,
+                clip->right - clip->left,
+                clip->bottom - clip->top);
 
         // 1x overdraw
-        mCaches.stencil.enableDebugTest(2);
+        mRenderState.stencil().enableDebugTest(2);
         drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
 
         // 2x overdraw
-        mCaches.stencil.enableDebugTest(3);
+        mRenderState.stencil().enableDebugTest(3);
         drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
 
         // 3x overdraw
-        mCaches.stencil.enableDebugTest(4);
+        mRenderState.stencil().enableDebugTest(4);
         drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
 
         // 4x overdraw and higher
-        mCaches.stencil.enableDebugTest(4, true);
+        mRenderState.stencil().enableDebugTest(4, true);
         drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
 
-        mCaches.stencil.disable();
+        mRenderState.stencil().disable();
     }
 }
 
@@ -556,11 +499,11 @@
 
 void OpenGLRenderer::flushLayerUpdates() {
     ATRACE_NAME("Update HW Layers");
-    syncState();
+    mRenderState.blend().syncEnabled();
     updateLayers();
     flushLayers();
     // Wait for all the layer updates to be executed
-    AutoFence fence;
+    glFinish();
 }
 
 void OpenGLRenderer::markLayersAsBuildLayers() {
@@ -753,7 +696,7 @@
         return false;
     }
 
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
     Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
     if (!layer) {
         return false;
@@ -835,8 +778,8 @@
     startTilingCurrentClip(true, true);
 
     // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
-    mCaches.enableScissor();
-    mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
+    mRenderState.scissor().setEnabled(true);
+    mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
             clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
     glClear(GL_COLOR_BUFFER_BIT);
 
@@ -863,7 +806,7 @@
     bool clipRequired = false;
     mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
             &clipRequired, nullptr, false); // safely ignore return, should never be rejected
-    mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+    mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
 
     if (fboLayer) {
         endTiling();
@@ -891,9 +834,9 @@
         layer->setAlpha(255);
     }
 
-    mCaches.unbindMeshBuffer();
+    mRenderState.meshState().unbindMeshBuffer();
 
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
 
     // When the layer is stored in an FBO, we can save a bit of fillrate by
     // drawing only the dirty region
@@ -960,7 +903,7 @@
     setupDrawTextureTransformUniforms(layer->getTexTransform());
     setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u);
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount);
 }
 
 void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
@@ -1002,7 +945,7 @@
         drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
                 layer->getTexture(), &layerPaint, blend,
                 &mMeshVertices[0].x, &mMeshVertices[0].u,
-                GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
+                GL_TRIANGLE_STRIP, kUnitQuadCount, swap, swap || simpleTransform);
 
         resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
     }
@@ -1168,7 +1111,7 @@
 
         numQuads++;
 
-        if (numQuads >= gMaxNumberOfQuads) {
+        if (numQuads >= kMaxNumberOfQuads) {
             DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
                     GL_UNSIGNED_SHORT, nullptr));
             numQuads = 0;
@@ -1261,7 +1204,7 @@
 void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
     GLsizei elementsCount = quadsCount * 6;
     while (elementsCount > 0) {
-        GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+        GLsizei drawCount = min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
 
         setupDrawIndexedVertices(&mesh[0].x);
         glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, nullptr);
@@ -1286,7 +1229,7 @@
         // The list contains bounds that have already been clipped
         // against their initial clip rect, and the current clip
         // is likely different so we need to disable clipping here
-        bool scissorChanged = mCaches.disableScissor();
+        bool scissorChanged = mRenderState.scissor().setEnabled(false);
 
         Vertex mesh[count * 4];
         Vertex* vertex = mesh;
@@ -1317,7 +1260,7 @@
 
         issueIndexedQuadDraw(&mesh[0], count);
 
-        if (scissorChanged) mCaches.enableScissor();
+        if (scissorChanged) mRenderState.scissor().setEnabled(true);
     } else {
         mLayers.clear();
     }
@@ -1406,7 +1349,8 @@
         writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight());
     }
     dirtyClip();
-    mCaches.setScissorEnabled(clipRect != nullptr || mScissorOptimizationDisabled);
+    bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled;
+    mRenderState.scissor().setEnabled(enableScissor);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1417,7 +1361,7 @@
     Rect clip(mState.currentClipRect());
     clip.snapToPixelBoundaries();
 
-    if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom,
+    if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
             clip.getWidth(), clip.getHeight())) {
         mState.setDirtyClip(false);
     }
@@ -1441,7 +1385,8 @@
         endTiling();
 
         RenderBuffer* buffer = mCaches.renderBufferCache.get(
-                Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
+                Stencil::getSmallestStencilFormat(),
+                layer->getWidth(), layer->getHeight());
         layer->setStencilRenderBuffer(buffer);
 
         startTiling(layer->clipRect, layer->layer.getHeight());
@@ -1491,7 +1436,7 @@
         }
     }
 
-    mCaches.setScissor(scissorBox.left, getViewportHeight() - scissorBox.bottom,
+    mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
             scissorBox.getWidth(), scissorBox.getHeight());
 
     const SkPaint* paint = nullptr;
@@ -1533,17 +1478,17 @@
                 incrementThreshold = 0;
             }
 
-            mCaches.stencil.enableWrite(incrementThreshold);
+            mRenderState.stencil().enableWrite(incrementThreshold);
 
             // Clean and update the stencil, but first make sure we restrict drawing
             // to the region's bounds
-            bool resetScissor = mCaches.enableScissor();
+            bool resetScissor = mRenderState.scissor().setEnabled(true);
             if (resetScissor) {
                 // The scissor was not set so we now need to update it
                 setScissorFromClip();
             }
 
-            mCaches.stencil.clear();
+            mRenderState.stencil().clear();
 
             // stash and disable the outline clip state, since stencil doesn't account for outline
             bool storedSkipOutlineClip = mSkipOutlineClip;
@@ -1564,10 +1509,10 @@
                 // so we don't want to dirty the current layer, if any
                 drawRegionRects(clipArea.getClipRegion(), paint, false);
             }
-            if (resetScissor) mCaches.disableScissor();
+            if (resetScissor) mRenderState.scissor().setEnabled(false);
             mSkipOutlineClip = storedSkipOutlineClip;
 
-            mCaches.stencil.enableTest(incrementThreshold);
+            mRenderState.stencil().enableTest(incrementThreshold);
 
             // Draw the region used to generate the stencil if the appropriate debug
             // mode is enabled
@@ -1580,7 +1525,7 @@
             }
         } else {
             EVENT_LOGD("setStencilFromClip - disabling");
-            mCaches.stencil.disable();
+            mRenderState.stencil().disable();
         }
     }
 }
@@ -1611,7 +1556,7 @@
     }
 
     // not quick rejected, so enable the scissor if clipRequired
-    mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+    mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
     mSkipOutlineClip = !roundRectClipRequired;
     return false;
 }
@@ -1627,6 +1572,18 @@
 #endif
 }
 
+void OpenGLRenderer::renderGlop(const Glop& glop) {
+    if (mState.getDirtyClip()) {
+        if (mRenderState.scissor().isEnabled()) {
+            setScissorFromClip();
+        }
+
+        setStencilFromClip();
+    }
+    mRenderState.render(glop);
+    dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Drawing commands
 ///////////////////////////////////////////////////////////////////////////////
@@ -1638,7 +1595,7 @@
     // Make sure setScissor & setStencil happen at the beginning of
     // this method
     if (mState.getDirtyClip()) {
-        if (mCaches.scissorEnabled) {
+        if (mRenderState.scissor().isEnabled()) {
             setScissorFromClip();
         }
 
@@ -1655,9 +1612,9 @@
 
     // Enable debug highlight when what we're about to draw is tested against
     // the stencil buffer and if stencil highlight debugging is on
-    mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
-            mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
-            mCaches.stencil.isTestEnabled();
+    mDescription.hasDebugHighlight = !mCaches.debugOverdraw
+            && mCaches.debugStencilClip == Caches::kStencilShowHighlight
+            && mRenderState.stencil().isTestEnabled();
 }
 
 void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1676,7 +1633,7 @@
 }
 
 void OpenGLRenderer::setupDrawNoTexture() {
-    mCaches.disableTexCoordsVertexArray();
+    mRenderState.meshState().disableTexCoordsVertexArray();
 }
 
 void OpenGLRenderer::setupDrawVertexAlpha(bool useShadowAlphaInterp) {
@@ -1717,7 +1674,7 @@
 
 void OpenGLRenderer::setupDrawShader(const SkShader* shader) {
     if (shader != nullptr) {
-        SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader);
+        SkiaShader::describe(&mCaches, mDescription, mCaches.extensions(), *shader);
     }
 }
 
@@ -1761,27 +1718,27 @@
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
-    blend |= (mColorSet && mColorA < 1.0f) ||
-            (getShader(paint) && !getShader(paint)->isOpaque()) ||
-            PaintUtils::isBlendedColorFilter(getColorFilter(paint));
+    blend |= (mColorSet && mColorA < 1.0f)
+            || (getShader(paint) && !getShader(paint)->isOpaque())
+            || PaintUtils::isBlendedColorFilter(getColorFilter(paint));
     chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
 void OpenGLRenderer::setupDrawProgram() {
-    useProgram(mCaches.programCache.get(mDescription));
+    mCaches.setProgram(mDescription);
     if (mDescription.hasRoundRectClip) {
         // TODO: avoid doing this repeatedly, stashing state pointer in program
         const RoundRectClipState* state = writableSnapshot()->roundRectClipState;
         const Rect& innerRect = state->innerRect;
-        glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"),
+        glUniform4f(mCaches.program().getUniform("roundRectInnerRectLTRB"),
                 innerRect.left, innerRect.top,
                 innerRect.right, innerRect.bottom);
-        glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"),
+        glUniformMatrix4fv(mCaches.program().getUniform("roundRectInvTransform"),
                 1, GL_FALSE, &state->matrix.data[0]);
 
         // add half pixel to round out integer rect space to cover pixel centers
         float roundedOutRadius = state->radius + 0.5f;
-        glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"),
+        glUniform1f(mCaches.program().getUniform("roundRectRadius"),
                 roundedOutRadius);
     }
 }
@@ -1799,7 +1756,8 @@
 
     bool dirty = right - left > 0.0f && bottom - top > 0.0f;
     const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform();
-    mCaches.currentProgram->set(writableSnapshot()->getOrthoMatrix(),
+
+    mCaches.program().set(currentSnapshot()->getOrthoMatrix(),
             mModelViewMatrix, transformMatrix, offset);
     if (dirty && mTrackDirtyRegions) {
         if (!ignoreTransform) {
@@ -1812,13 +1770,13 @@
 
 void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
     if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
-        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
+        mCaches.program().setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
 
 void OpenGLRenderer::setupDrawPureColorUniforms() {
     if (mSetShaderColor) {
-        mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
+        mCaches.program().setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
 
@@ -1837,7 +1795,8 @@
         mModelViewMatrix.load(modelViewWithoutTransform);
     }
 
-    SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader);
+    SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit,
+            mCaches.extensions(), *shader);
 }
 
 void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) {
@@ -1853,7 +1812,7 @@
         const GLfloat r = a * SkColorGetR(color) / 255.0f;
         const GLfloat g = a * SkColorGetG(color) / 255.0f;
         const GLfloat b = a * SkColorGetB(color) / 255.0f;
-        glUniform4f(mCaches.currentProgram->getUniform("colorBlend"), r, g, b, a);
+        glUniform4f(mCaches.program().getUniform("colorBlend"), r, g, b, a);
         return;
     }
 
@@ -1874,9 +1833,9 @@
         colorVector[2] = srcColorMatrix[14] / 255.0f;
         colorVector[3] = srcColorMatrix[19] / 255.0f;
 
-        glUniformMatrix4fv(mCaches.currentProgram->getUniform("colorMatrix"), 1,
+        glUniformMatrix4fv(mCaches.program().getUniform("colorMatrix"), 1,
                 GL_FALSE, colorMatrix);
-        glUniform4fv(mCaches.currentProgram->getUniform("colorMatrixVector"), 1, colorVector);
+        glUniform4fv(mCaches.program().getUniform("colorMatrixVector"), 1, colorVector);
         return;
     }
 
@@ -1884,25 +1843,25 @@
 }
 
 void OpenGLRenderer::setupDrawTextGammaUniforms() {
-    mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
+    mCaches.fontRenderer->setupProgram(mDescription, mCaches.program());
 }
 
 void OpenGLRenderer::setupDrawSimpleMesh() {
-    bool force = mCaches.bindMeshBuffer();
-    mCaches.bindPositionVertexPointer(force, nullptr);
-    mCaches.unbindIndicesBuffer();
+    bool force = mRenderState.meshState().bindMeshBuffer();
+    mRenderState.meshState().bindPositionVertexPointer(force, nullptr);
+    mRenderState.meshState().unbindIndicesBuffer();
 }
 
 void OpenGLRenderer::setupDrawTexture(GLuint texture) {
-    if (texture) bindTexture(texture);
+    if (texture) mCaches.textureState().bindTexture(texture);
     mTextureUnit++;
-    mCaches.enableTexCoordsVertexArray();
+    mRenderState.meshState().enableTexCoordsVertexArray();
 }
 
 void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
-    bindExternalTexture(texture);
+    mCaches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
     mTextureUnit++;
-    mCaches.enableTexCoordsVertexArray();
+    mRenderState.meshState().enableTexCoordsVertexArray();
 }
 
 void OpenGLRenderer::setupDrawTextureTransform() {
@@ -1910,7 +1869,7 @@
 }
 
 void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
-    glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
+    glUniformMatrix4fv(mCaches.program().getUniform("mainTextureTransform"), 1,
             GL_FALSE, &transform.data[0]);
 }
 
@@ -1918,35 +1877,35 @@
         const GLvoid* texCoords, GLuint vbo) {
     bool force = false;
     if (!vertices || vbo) {
-        force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+        force = mRenderState.meshState().bindMeshBuffer(vbo);
     } else {
-        force = mCaches.unbindMeshBuffer();
+        force = mRenderState.meshState().unbindMeshBuffer();
     }
 
-    mCaches.bindPositionVertexPointer(force, vertices);
-    if (mCaches.currentProgram->texCoords >= 0) {
-        mCaches.bindTexCoordsVertexPointer(force, texCoords);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices);
+    if (mCaches.program().texCoords >= 0) {
+        mRenderState.meshState().bindTexCoordsVertexPointer(force, texCoords);
     }
 
-    mCaches.unbindIndicesBuffer();
+    mRenderState.meshState().unbindIndicesBuffer();
 }
 
 void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices,
         const GLvoid* texCoords, const GLvoid* colors) {
-    bool force = mCaches.unbindMeshBuffer();
+    bool force = mRenderState.meshState().unbindMeshBuffer();
     GLsizei stride = sizeof(ColorTextureVertex);
 
-    mCaches.bindPositionVertexPointer(force, vertices, stride);
-    if (mCaches.currentProgram->texCoords >= 0) {
-        mCaches.bindTexCoordsVertexPointer(force, texCoords, stride);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices, stride);
+    if (mCaches.program().texCoords >= 0) {
+        mRenderState.meshState().bindTexCoordsVertexPointer(force, texCoords, stride);
     }
-    int slot = mCaches.currentProgram->getAttrib("colors");
+    int slot = mCaches.program().getAttrib("colors");
     if (slot >= 0) {
         glEnableVertexAttribArray(slot);
         glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors);
     }
 
-    mCaches.unbindIndicesBuffer();
+    mRenderState.meshState().unbindIndicesBuffer();
 }
 
 void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices,
@@ -1954,24 +1913,24 @@
     bool force = false;
     // If vbo is != 0 we want to treat the vertices parameter as an offset inside
     // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
-    // use the default VBO found in Caches
+    // use the default VBO found in RenderState
     if (!vertices || vbo) {
-        force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+        force = mRenderState.meshState().bindMeshBuffer(vbo);
     } else {
-        force = mCaches.unbindMeshBuffer();
+        force = mRenderState.meshState().unbindMeshBuffer();
     }
-    mCaches.bindQuadIndicesBuffer();
+    mRenderState.meshState().bindQuadIndicesBuffer();
 
-    mCaches.bindPositionVertexPointer(force, vertices);
-    if (mCaches.currentProgram->texCoords >= 0) {
-        mCaches.bindTexCoordsVertexPointer(force, texCoords);
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices);
+    if (mCaches.program().texCoords >= 0) {
+        mRenderState.meshState().bindTexCoordsVertexPointer(force, texCoords);
     }
 }
 
 void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
-    bool force = mCaches.unbindMeshBuffer();
-    mCaches.bindQuadIndicesBuffer();
-    mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
+    bool force = mRenderState.meshState().unbindMeshBuffer();
+    mRenderState.meshState().bindQuadIndicesBuffer();
+    mRenderState.meshState().bindPositionVertexPointer(force, vertices, kVertexStride);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2030,8 +1989,8 @@
     // No need to check for a UV mapper on the texture object, only ARGB_8888
     // bitmaps get packed in the atlas
     drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
-            paint, (GLvoid*) nullptr, (GLvoid*) gMeshTextureOffset,
-            GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+            paint, (GLvoid*) nullptr, (GLvoid*) kMeshTextureOffset,
+            GL_TRIANGLE_STRIP, kUnitQuadCount, ignoreTransform);
 }
 
 /**
@@ -2042,7 +2001,7 @@
 void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
         int bitmapCount, TextureVertex* vertices, bool pureTranslate,
         const Rect& bounds, const SkPaint* paint) {
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
     Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
     if (!texture) return;
 
@@ -2073,7 +2032,7 @@
         return;
     }
 
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
     Texture* texture = getTexture(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
@@ -2094,7 +2053,7 @@
     }
 
     // TODO: use quickReject on bounds from vertices
-    mCaches.enableScissor();
+    mRenderState.scissor().setEnabled(true);
 
     float left = FLT_MAX;
     float top = FLT_MAX;
@@ -2114,7 +2073,7 @@
         colors = tempColors.get();
     }
 
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
     Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
     const UvMapper& mapper(getMapper(texture));
 
@@ -2185,7 +2144,7 @@
     setupDrawBlending(paint, true);
     setupDrawProgram();
     setupDrawDirtyRegionsDisabled();
-    setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f);
+    setupDrawModelView(kModelViewMode_Translate, false, 0, 0, 0, 0);
     setupDrawTexture(texture->id);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
@@ -2193,7 +2152,7 @@
 
     glDrawArrays(GL_TRIANGLES, 0, count);
 
-    int slot = mCaches.currentProgram->getAttrib("colors");
+    int slot = mCaches.program().getAttrib("colors");
     if (slot >= 0) {
         glDisableVertexAttribArray(slot);
     }
@@ -2209,7 +2168,7 @@
         return;
     }
 
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
     Texture* texture = getTexture(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
@@ -2224,7 +2183,7 @@
 
     getMapper(texture).map(u1, v1, u2, v2);
 
-    mCaches.unbindMeshBuffer();
+    mRenderState.meshState().unbindMeshBuffer();
     resetDrawTextureTexCoords(u1, v1, u2, v2);
 
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
@@ -2271,12 +2230,12 @@
         drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
                 texture->id, paint,
                 &mMeshVertices[0].x, &mMeshVertices[0].u,
-                GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+                GL_TRIANGLE_STRIP, kUnitQuadCount, ignoreTransform);
     } else {
         drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
                 texture->id, paint, texture->blend,
                 &mMeshVertices[0].x, &mMeshVertices[0].u,
-                GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
+                GL_TRIANGLE_STRIP, kUnitQuadCount, false, ignoreTransform);
     }
 
     if (CC_UNLIKELY(useScaleTransform)) {
@@ -2309,7 +2268,7 @@
     }
 
     if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
-        mCaches.activeTexture(0);
+        mCaches.textureState().activateTexture(0);
         Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
         if (!texture) return;
         const AutoTexture autoCleanup(texture);
@@ -2363,7 +2322,7 @@
  */
 void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
         TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) {
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
     Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
@@ -2386,17 +2345,33 @@
         return;
     }
 
+    if (!paint->getShader() && !currentSnapshot()->roundRectClipState) {
+        Glop glop;
+        GlopBuilder aBuilder(mRenderState, mCaches, &glop);
+        bool fudgeOffset = displayFlags & kVertexBuffer_Offset;
+        bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp;
+        aBuilder.setMeshVertexBuffer(vertexBuffer, shadowInterp)
+                .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset)
+                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+                .setPaint(paint, currentSnapshot()->alpha)
+                .build();
+        renderGlop(glop);
+        return;
+    }
+
+
+    const VertexBuffer::MeshFeatureFlags meshFeatureFlags = vertexBuffer.getMeshFeatureFlags();
     Rect bounds(vertexBuffer.getBounds());
     bounds.translate(translateX, translateY);
     dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
 
     int color = paint->getColor();
-    bool isAA = paint->isAntiAlias();
+    bool isAA = meshFeatureFlags & VertexBuffer::kAlpha;
 
     setupDraw();
     setupDrawNoTexture();
     if (isAA) setupDrawVertexAlpha((displayFlags & kVertexBuffer_ShadowInterp));
-    setupDrawColor(color, ((color >> 24) & 0xFF) * writableSnapshot()->alpha);
+    setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
     setupDrawColorFilter(getColorFilter(paint));
     setupDrawShader(getShader(paint));
     setupDrawBlending(paint, isAA);
@@ -2408,35 +2383,27 @@
     setupDrawShaderUniforms(getShader(paint));
 
     const void* vertices = vertexBuffer.getBuffer();
-    mCaches.unbindMeshBuffer();
-    mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
-    mCaches.resetTexCoordsVertexPointer();
+    mRenderState.meshState().unbindMeshBuffer();
+    mRenderState.meshState().bindPositionVertexPointer(true, vertices,
+            isAA ? kAlphaVertexStride : kVertexStride);
+    mRenderState.meshState().resetTexCoordsVertexPointer();
 
     int alphaSlot = -1;
     if (isAA) {
-        void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
-        alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
+        void* alphaCoords = ((GLbyte*) vertices) + kVertexAlphaOffset;
+        alphaSlot = mCaches.program().getAttrib("vtxAlpha");
         // TODO: avoid enable/disable in back to back uses of the alpha attribute
         glEnableVertexAttribArray(alphaSlot);
-        glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
+        glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords);
     }
 
-    const VertexBuffer::Mode mode = vertexBuffer.getMode();
-    if (mode == VertexBuffer::kStandard) {
-        mCaches.unbindIndicesBuffer();
-        glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
-    } else if (mode == VertexBuffer::kOnePolyRingShadow) {
-        mCaches.bindShadowIndicesBuffer();
-        glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT,
-                GL_UNSIGNED_SHORT, nullptr);
-    } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
-        mCaches.bindShadowIndicesBuffer();
-        glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT,
-                GL_UNSIGNED_SHORT, nullptr);
-    } else if (mode == VertexBuffer::kIndices) {
-        mCaches.unbindIndicesBuffer();
+    if (meshFeatureFlags & VertexBuffer::kIndices) {
+        mRenderState.meshState().unbindIndicesBuffer();
         glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(),
                 GL_UNSIGNED_SHORT, vertexBuffer.getIndices());
+    } else {
+        mRenderState.meshState().unbindIndicesBuffer();
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
     }
 
     if (isAA) {
@@ -2547,7 +2514,7 @@
     }
 
     if (p->getPathEffect() != nullptr) {
-        mCaches.activeTexture(0);
+        mCaches.textureState().activateTexture(0);
         const PathTexture* texture = mCaches.pathCache.getRoundRect(
                 right - left, bottom - top, rx, ry, p);
         drawShape(left, top, texture, p);
@@ -2565,7 +2532,7 @@
         return;
     }
     if (p->getPathEffect() != nullptr) {
-        mCaches.activeTexture(0);
+        mCaches.textureState().activateTexture(0);
         const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
         drawShape(x - radius, y - radius, texture, p);
     } else {
@@ -2588,7 +2555,7 @@
     }
 
     if (p->getPathEffect() != nullptr) {
-        mCaches.activeTexture(0);
+        mCaches.textureState().activateTexture(0);
         const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
         drawShape(left, top, texture, p);
     } else {
@@ -2612,7 +2579,7 @@
 
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
     if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
-        mCaches.activeTexture(0);
+        mCaches.textureState().activateTexture(0);
         const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
                 startAngle, sweepAngle, useCenter, p);
         drawShape(left, top, texture, p);
@@ -2649,7 +2616,7 @@
         // only fill style is supported by drawConvexPath, since others have to handle joins
         if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
                 p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
-            mCaches.activeTexture(0);
+            mCaches.textureState().activateTexture(0);
             const PathTexture* texture =
                     mCaches.pathCache.getRect(right - left, bottom - top, p);
             drawShape(left, top, texture, p);
@@ -2678,7 +2645,7 @@
 void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
         int bytesCount, int count, const float* positions,
         FontRenderer& fontRenderer, int alpha, float x, float y) {
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
 
     TextShadow textShadow;
     if (!getTextShadow(paint, &textShadow)) {
@@ -2716,9 +2683,9 @@
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawShaderUniforms(getShader(paint));
-    setupDrawMesh(nullptr, (GLvoid*) gMeshTextureOffset);
+    setupDrawMesh(nullptr, (GLvoid*) kMeshTextureOffset);
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount);
 }
 
 bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
@@ -2738,7 +2705,7 @@
         return;
     }
 
-    mCaches.enableScissor();
+    mRenderState.scissor().setEnabled(true);
 
     float x = 0.0f;
     float y = 0.0f;
@@ -2868,8 +2835,6 @@
     mState.setClippingRoundRect(allocator, rect, radius, highPriority);
 }
 
-
-
 void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
         const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
         DrawOpMode drawOpMode) {
@@ -2964,7 +2929,7 @@
     }
 
     // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
-    mCaches.enableScissor();
+    mRenderState.scissor().setEnabled(true);
 
     FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
     fontRenderer.setFont(paint, SkMatrix::I());
@@ -2994,7 +2959,7 @@
 void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
     if (mState.currentlyIgnored()) return;
 
-    mCaches.activeTexture(0);
+    mCaches.textureState().activateTexture(0);
 
     const PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return;
@@ -3038,8 +3003,8 @@
 
     updateLayer(layer, true);
 
-    mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
-    mCaches.activeTexture(0);
+    mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
+    mCaches.textureState().activateTexture(0);
 
     if (CC_LIKELY(!layer->region.isEmpty())) {
         if (layer->region.isRect()) {
@@ -3074,7 +3039,7 @@
             GLsizei elementsCount = layer->meshElementCount;
 
             while (elementsCount > 0) {
-                GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+                GLsizei drawCount = min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
 
                 setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
                 DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
@@ -3152,9 +3117,9 @@
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawShaderUniforms(getShader(paint));
-    setupDrawMesh(nullptr, (GLvoid*) gMeshTextureOffset);
+    setupDrawMesh(nullptr, (GLvoid*) kMeshTextureOffset);
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount);
 }
 
 // Same values used by Skia
@@ -3220,7 +3185,7 @@
     if (mState.currentlyIgnored()) return;
 
     // TODO: use quickRejectWithScissor. For now, always force enable scissor.
-    mCaches.enableScissor();
+    mRenderState.scissor().setEnabled(true);
 
     SkPaint paint;
     paint.setAntiAlias(true); // want to use AlphaVertex
@@ -3313,6 +3278,20 @@
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         const SkPaint* paint, bool ignoreTransform) {
+
+    if (!paint->getShader() && !currentSnapshot()->roundRectClipState) {
+        const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform();
+        Glop glop;
+        GlopBuilder aBuilder(mRenderState, mCaches, &glop);
+        aBuilder.setMeshUnitQuad()
+                .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false)
+                .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
+                .setPaint(paint, currentSnapshot()->alpha)
+                .build();
+        renderGlop(glop);
+        return;
+    }
+
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
     if (getShader(paint)) {
@@ -3333,7 +3312,7 @@
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawSimpleMesh();
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, kUnitQuadCount);
 }
 
 void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
@@ -3341,7 +3320,7 @@
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
 
     GLvoid* vertices = (GLvoid*) nullptr;
-    GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
+    GLvoid* texCoords = (GLvoid*) kMeshTextureOffset;
 
     if (texture->uvMapper) {
         vertices = &mMeshVertices[0].x;
@@ -3360,11 +3339,11 @@
         texture->setFilter(GL_NEAREST, true);
         drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
                 paint, texture->blend, vertices, texCoords,
-                GL_TRIANGLE_STRIP, gMeshCount, false, true);
+                GL_TRIANGLE_STRIP, kUnitQuadCount, false, true);
     } else {
         texture->setFilter(getFilter(paint), true);
         drawTextureMesh(left, top, right, bottom, texture->id, paint,
-                texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
+                texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, kUnitQuadCount);
     }
 
     if (texture->uvMapper) {
@@ -3459,7 +3438,7 @@
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
         ProgramDescription& description, bool swapSrcDst) {
 
-    if (writableSnapshot()->roundRectClipState != nullptr /*&& !mSkipOutlineClip*/) {
+    if (currentSnapshot()->roundRectClipState != nullptr /*&& !mSkipOutlineClip*/) {
         blend = true;
         mDescription.hasRoundRectClip = true;
     }
@@ -3474,47 +3453,20 @@
         // If the blend mode cannot be implemented using shaders, fall
         // back to the default SrcOver blend mode instead
         if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
-            if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) {
+            if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
                 description.framebufferMode = mode;
                 description.swapSrcDst = swapSrcDst;
 
-                if (mCaches.blend) {
-                    glDisable(GL_BLEND);
-                    mCaches.blend = false;
-                }
-
+                mRenderState.blend().disable();
                 return;
             } else {
                 mode = SkXfermode::kSrcOver_Mode;
             }
         }
-
-        if (!mCaches.blend) {
-            glEnable(GL_BLEND);
-        }
-
-        GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
-        GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
-
-        if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
-            glBlendFunc(sourceMode, destMode);
-            mCaches.lastSrcMode = sourceMode;
-            mCaches.lastDstMode = destMode;
-        }
-    } else if (mCaches.blend) {
-        glDisable(GL_BLEND);
+        mRenderState.blend().enable(mode, swapSrcDst);
+    } else {
+        mRenderState.blend().disable();
     }
-    mCaches.blend = blend;
-}
-
-bool OpenGLRenderer::useProgram(Program* program) {
-    if (!program->isInUse()) {
-        if (mCaches.currentProgram != nullptr) mCaches.currentProgram->remove();
-        program->use();
-        mCaches.currentProgram = program;
-        return false;
-    }
-    return true;
 }
 
 void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 76ad6ce..f097041 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -57,6 +57,7 @@
 namespace uirenderer {
 
 class DeferredDisplayState;
+struct Glop;
 class RenderState;
 class RenderNode;
 class TextSetupFunctor;
@@ -222,6 +223,10 @@
         return mCaches;
     }
 
+    RenderState& renderState() {
+        return mRenderState;
+    }
+
     int getViewportWidth() { return mState.getViewportWidth(); }
     int getViewportHeight() { return mState.getViewportHeight(); }
 
@@ -523,11 +528,13 @@
         return false;
     }
 
-    inline RenderState& renderState() { return mRenderState; }
-
     CanvasState mState;
+    Caches& mCaches;
+    RenderState& mRenderState;
 
 private:
+    void renderGlop(const Glop& glop);
+
     /**
      * Discards the content of the framebuffer if supported by the driver.
      * This method should be called at the beginning of a frame to optimize
@@ -536,12 +543,6 @@
     void discardFramebuffer(float left, float top, float right, float bottom);
 
     /**
-     * Ensures the state of the renderer is the same as the state of
-     * the GL context.
-     */
-    void syncState();
-
-    /**
      * Tells the GPU what part of the screen is about to be redrawn.
      * This method will use the current layer space clip rect.
      * This method needs to be invoked every time getTargetFbo() is
@@ -847,22 +848,6 @@
     bool canSkipText(const SkPaint* paint) const;
 
     /**
-     * Binds the specified texture. The texture unit must have been selected
-     * prior to calling this method.
-     */
-    inline void bindTexture(GLuint texture) {
-        mCaches.bindTexture(texture);
-    }
-
-    /**
-     * Binds the specified EGLImage texture. The texture unit must have been selected
-     * prior to calling this method.
-     */
-    inline void bindExternalTexture(GLuint texture) {
-        mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
-    }
-
-    /**
      * Enable or disable blending as necessary. This function sets the appropriate
      * blend function based on the specified xfermode.
      */
@@ -870,18 +855,6 @@
             bool swapSrcDst = false);
 
     /**
-     * Use the specified program with the current GL context. If the program is already
-     * in use, it will not be bound again. If it is not in use, the current program is
-     * marked unused and the specified program becomes used and becomes the new
-     * current program.
-     *
-     * @param program The program to use
-     *
-     * @return true If the specified program was already in use, false otherwise.
-     */
-    inline bool useProgram(Program* program);
-
-    /**
      * Invoked before any drawing operation. This sets required state.
      */
     void setupDraw(bool clear = true);
@@ -1027,11 +1000,6 @@
     DrawModifiers mDrawModifiers;
     SkPaint mFilteredPaint;
 
-    // Various caches
-    Caches& mCaches;
-    Extensions& mExtensions;
-    RenderState& mRenderState;
-
     // List of rectangles to clear after saveLayer() is invoked
     std::vector<Rect> mLayers;
     // List of layers to update at the beginning of a frame
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index eb88bc0..af403b4 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -23,6 +23,7 @@
 #include "Patch.h"
 #include "PatchCache.h"
 #include "Properties.h"
+#include "renderstate/RenderState.h"
 
 namespace android {
 namespace uirenderer {
@@ -31,9 +32,13 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-PatchCache::PatchCache():
-        mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
-        mMeshBuffer(0), mFreeBlocks(nullptr), mGenerationId(0) {
+PatchCache::PatchCache(RenderState& renderState)
+        : mRenderState(renderState)
+        , mSize(0)
+        , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
+        , mMeshBuffer(0)
+        , mFreeBlocks(nullptr)
+        , mGenerationId(0) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) {
         INIT_LOGD("  Setting patch cache size to %skB", property);
@@ -48,15 +53,15 @@
     clear();
 }
 
-void PatchCache::init(Caches& caches) {
+void PatchCache::init() {
     bool created = false;
     if (!mMeshBuffer) {
         glGenBuffers(1, &mMeshBuffer);
         created = true;
     }
 
-    caches.bindMeshBuffer(mMeshBuffer);
-    caches.resetVertexPointers();
+    mRenderState.meshState().bindMeshBuffer(mMeshBuffer);
+    mRenderState.meshState().resetVertexPointers();
 
     if (created) {
         createVertexBuffer();
@@ -85,7 +90,7 @@
     clearCache();
 
     if (mMeshBuffer) {
-        Caches::getInstance().unbindMeshBuffer();
+        mRenderState.meshState().unbindMeshBuffer();
         glDeleteBuffers(1, &mMeshBuffer);
         mMeshBuffer = 0;
         mSize = 0;
@@ -187,7 +192,7 @@
  */
 void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
     // This call ensures the VBO exists and that it is bound
-    init(Caches::getInstance());
+    init();
 
     // If we're running out of space, let's clear the entire cache
     uint32_t size = newMesh->getSize();
@@ -219,7 +224,7 @@
 
     // Copy the 9patch mesh in the VBO
     newMesh->offset = (GLintptr) (block->offset);
-    newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+    newMesh->textureOffset = newMesh->offset + kMeshTextureOffset;
     glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
 
     // Remove the block since we've used it entirely
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 4cb5338..e038720 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -51,9 +51,9 @@
 
 class PatchCache {
 public:
-    PatchCache();
+    PatchCache(RenderState& renderState);
     ~PatchCache();
-    void init(Caches& caches);
+    void init();
 
     const Patch* get(const AssetAtlas::Entry* entry,
             const uint32_t bitmapWidth, const uint32_t bitmapHeight,
@@ -168,6 +168,7 @@
     void dumpFreeBlocks(const char* prefix);
 #endif
 
+    RenderState& mRenderState;
     uint32_t mMaxSize;
     uint32_t mSize;
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index c564b87..e2caf8b 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -31,7 +31,6 @@
 #include "PathCache.h"
 
 #include "thread/Signal.h"
-#include "thread/Task.h"
 #include "thread/TaskProcessor.h"
 
 namespace android {
@@ -231,7 +230,7 @@
         }
 
         if (texture->id) {
-            Caches::getInstance().deleteTexture(texture->id);
+            Caches::getInstance().textureState().deleteTexture(texture->id);
         }
         delete texture;
     }
@@ -313,7 +312,7 @@
 
     glGenTextures(1, &texture->id);
 
-    Caches::getInstance().bindTexture(texture->id);
+    Caches::getInstance().textureState().bindTexture(texture->id);
     // Textures are Alpha8
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
@@ -493,7 +492,9 @@
         if (mProcessor == nullptr) {
             mProcessor = new PathProcessor(Caches::getInstance());
         }
-        mProcessor->add(task);
+        if (!mProcessor->add(task)) {
+            mProcessor->process(task);
+        }
     }
 }
 
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index ecd3712..7378018 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -17,21 +17,22 @@
 #ifndef ANDROID_HWUI_PATH_CACHE_H
 #define ANDROID_HWUI_PATH_CACHE_H
 
-#include <GLES2/gl2.h>
+#include "Debug.h"
+#include "Texture.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
+#include "utils/Macros.h"
+#include "utils/Pair.h"
 
+#include <GLES2/gl2.h>
+#include <SkPath.h>
 #include <utils/LruCache.h>
 #include <utils/Mutex.h>
 #include <utils/Vector.h>
 
-#include "Debug.h"
-#include "Texture.h"
-#include "utils/Macros.h"
-#include "utils/Pair.h"
-
 class SkBitmap;
 class SkCanvas;
 class SkPaint;
-class SkPath;
 struct SkRect;
 
 namespace android {
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index ceec4fc..3d8a749 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -783,6 +783,7 @@
     Rect bounds(path.getBounds());
     paintInfo.expandBoundsForStroke(&bounds);
     vertexBuffer.setBounds(bounds);
+    vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
 }
 
 template <class TYPE>
@@ -840,6 +841,7 @@
     // expand bounds from vertex coords to pixel data
     paintInfo.expandBoundsForStroke(&bounds);
     vertexBuffer.setBounds(bounds);
+    vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
 }
 
 void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
@@ -890,6 +892,7 @@
     // expand bounds from vertex coords to pixel data
     paintInfo.expandBoundsForStroke(&bounds);
     vertexBuffer.setBounds(bounds);
+    vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index efa271e..9665a68 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -16,13 +16,14 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <utils/Log.h>
+#include "PixelBuffer.h"
 
-#include "Caches.h"
 #include "Debug.h"
 #include "Extensions.h"
-#include "PixelBuffer.h"
 #include "Properties.h"
+#include "renderstate/RenderState.h"
+
+#include <utils/Log.h>
 
 namespace android {
 namespace uirenderer {
@@ -93,14 +94,16 @@
     Caches& mCaches;
 };
 
-GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
+GpuPixelBuffer::GpuPixelBuffer(GLenum format,
+        uint32_t width, uint32_t height)
         : PixelBuffer(format, width, height)
         , mMappedPointer(nullptr)
-        , mCaches(Caches::getInstance()) {
+        , mCaches(Caches::getInstance()){
     glGenBuffers(1, &mBuffer);
-    mCaches.bindPixelBuffer(mBuffer);
+
+    mCaches.pixelBufferState().bind(mBuffer);
     glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
-    mCaches.unbindPixelBuffer();
+    mCaches.pixelBufferState().unbind();
 }
 
 GpuPixelBuffer::~GpuPixelBuffer() {
@@ -109,7 +112,7 @@
 
 uint8_t* GpuPixelBuffer::map(AccessMode mode) {
     if (mAccessMode == kAccessMode_None) {
-        mCaches.bindPixelBuffer(mBuffer);
+        mCaches.pixelBufferState().bind(mBuffer);
         mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
 #if DEBUG_OPENGL
         if (!mMappedPointer) {
@@ -128,7 +131,7 @@
 void GpuPixelBuffer::unmap() {
     if (mAccessMode != kAccessMode_None) {
         if (mMappedPointer) {
-            mCaches.bindPixelBuffer(mBuffer);
+            mCaches.pixelBufferState().bind(mBuffer);
             GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
             if (status == GL_FALSE) {
                 ALOGE("Corrupted GPU pixel buffer");
@@ -145,7 +148,7 @@
 
 void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
     // If the buffer is not mapped, unmap() will not bind it
-    mCaches.bindPixelBuffer(mBuffer);
+    mCaches.pixelBufferState().bind(mBuffer);
     unmap();
     glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
             GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
@@ -155,7 +158,8 @@
 // Factory
 ///////////////////////////////////////////////////////////////////////////////
 
-PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
+PixelBuffer* PixelBuffer::create(GLenum format,
+        uint32_t width, uint32_t height, BufferType type) {
     if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
         return new GpuPixelBuffer(format, width, height);
     }
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 04225a2..aac5ec4 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -18,6 +18,7 @@
 #define ANDROID_HWUI_PIXEL_BUFFER_H
 
 #include <GLES3/gl3.h>
+#include <cutils/log.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index fb07dfa..5f34b34 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -46,7 +46,7 @@
             glAttachShader(mProgramId, mVertexShader);
             glAttachShader(mProgramId, mFragmentShader);
 
-            position = bindAttrib("position", kBindingPosition);
+            bindAttrib("position", kBindingPosition);
             if (description.hasTexture || description.hasExternalTexture) {
                 texCoords = bindAttrib("texCoords", kBindingTexCoords);
             } else {
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index d05b331..01231b5 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -102,7 +102,7 @@
  * A ProgramDescription must be used in conjunction with a ProgramCache.
  */
 struct ProgramDescription {
-    enum ColorModifier {
+    enum ColorFilterMode {
         kColorNone = 0,
         kColorMatrix,
         kColorBlend
@@ -148,7 +148,7 @@
     GLenum bitmapWrapT;
 
     // Color operations
-    ColorModifier colorOp;
+    ColorFilterMode colorOp;
     SkXfermode::Mode colorMode;
 
     // Framebuffer blending (requires Extensions.hasFramebufferFetch())
@@ -366,12 +366,7 @@
     void setColor(const float r, const float g, const float b, const float a);
 
     /**
-     * Name of the position attribute.
-     */
-    int position;
-
-    /**
-     * Name of the texCoords attribute if it exists, -1 otherwise.
+     * Name of the texCoords attribute if it exists (kBindingTexCoords), -1 otherwise.
      */
     int texCoords;
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 3bbd520..8c6a91cc 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -404,7 +404,8 @@
 // Constructors/destructors
 ///////////////////////////////////////////////////////////////////////////////
 
-ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) {
+ProgramCache::ProgramCache(Extensions& extensions)
+        : mHasES3(extensions.getMajorGlVersion() >= 3) {
 }
 
 ProgramCache::~ProgramCache() {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 30fa0df..1dadda1 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -40,7 +40,7 @@
  */
 class ProgramCache {
 public:
-    ProgramCache();
+    ProgramCache(Extensions& extensions);
     ~ProgramCache();
 
     Program* get(const ProgramDescription& description);
diff --git a/libs/hwui/Query.h b/libs/hwui/Query.h
deleted file mode 100644
index e25b16b..0000000
--- a/libs/hwui/Query.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_QUERY_H
-#define ANDROID_HWUI_QUERY_H
-
-#include <GLES3/gl3.h>
-
-#include "Extensions.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A Query instance can be used to perform occlusion queries. If the device
- * does not support occlusion queries, the result of a query will always be
- * 0 and the result will always be marked available.
- *
- * To run an occlusion query successfully, you must start end end the query:
- *
- * Query query;
- * query.begin();
- * // execute OpenGL calls
- * query.end();
- * GLuint result = query.getResult();
- */
-class Query {
-public:
-    /**
-     * Possible query targets.
-     */
-    enum Target {
-        /**
-         * Indicates if any sample passed the depth & stencil tests.
-         */
-        kTargetSamples = GL_ANY_SAMPLES_PASSED,
-        /**
-         * Indicates if any sample passed the depth & stencil tests.
-         * The implementation may choose to use a less precise version
-         * of the test, potentially resulting in false positives.
-         */
-        kTargetConservativeSamples = GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
-    };
-
-    /**
-     * Creates a new query with the specified target. The default
-     * target is kTargetSamples (of GL_ANY_SAMPLES_PASSED in OpenGL.)
-     */
-    Query(Target target = kTargetSamples): mActive(false), mTarget(target),
-            mCanQuery(Extensions::getInstance().hasOcclusionQueries()),
-            mQuery(0) {
-    }
-
-    ~Query() {
-        if (mQuery) {
-            glDeleteQueries(1, &mQuery);
-        }
-    }
-
-    /**
-     * Begins the query. If the query has already begun or if the device
-     * does not support occlusion queries, calling this method as no effect.
-     * After calling this method successfully, the query is marked active.
-     */
-    void begin() {
-        if (!mActive && mCanQuery) {
-            if (!mQuery) {
-                glGenQueries(1, &mQuery);
-            }
-
-            glBeginQuery(mTarget, mQuery);
-            mActive = true;
-        }
-    }
-
-    /**
-     * Ends the query. If the query has already begun or if the device
-     * does not support occlusion queries, calling this method as no effect.
-     * After calling this method successfully, the query is marked inactive.
-     */
-    void end() {
-        if (mQuery && mActive) {
-            glEndQuery(mTarget);
-            mActive = false;
-        }
-    }
-
-    /**
-     * Returns true if the query is active, false otherwise.
-     */
-    bool isActive() {
-        return mActive;
-    }
-
-    /**
-     * Returns true if the result of the query is available,
-     * false otherwise. Calling getResult() before the result
-     * is available may result in the calling thread being blocked.
-     * If the device does not support queries, this method always
-     * returns true.
-     */
-    bool isResultAvailable() {
-        if (!mQuery) return true;
-
-        GLuint result;
-        glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &result);
-        return result == GL_TRUE;
-    }
-
-    /**
-     * Returns the result of the query. If the device does not
-     * support queries this method will return 0.
-     *
-     * Calling this method implicitely calls end() if the query
-     * is currently active.
-     */
-    GLuint getResult() {
-        if (!mQuery) return 0;
-
-        end();
-
-        GLuint result;
-        glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT, &result);
-        return result;
-    }
-
-
-private:
-    bool mActive;
-    GLenum mTarget;
-    bool mCanQuery;
-    GLuint mQuery;
-
-}; // class Query
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_QUERY_H
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a0a5a1c..659ef6c 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -836,7 +836,6 @@
  */
 template <class T>
 void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
-    const int level = handler.level();
     if (mDisplayListData->isEmpty()) {
         DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
         return;
@@ -860,7 +859,7 @@
 #if DEBUG_DISPLAY_LIST
     const Rect& clipRect = renderer.getLocalClipBounds();
     DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
-            level * 2, "", this, getName(),
+            handler.level() * 2, "", this, getName(),
             clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
 #endif
 
@@ -900,7 +899,7 @@
                 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
                     DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
 #if DEBUG_DISPLAY_LIST
-                    op->output(level + 1);
+                    op->output(handler.level() + 1);
 #endif
                     handler(op, saveCountOffset, properties().getClipToBounds());
 
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
deleted file mode 100644
index 45a97fb..0000000
--- a/libs/hwui/RenderState.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "RenderState.h"
-
-#include "renderthread/CanvasContext.h"
-#include "renderthread/EglManager.h"
-
-namespace android {
-namespace uirenderer {
-
-RenderState::RenderState(renderthread::RenderThread& thread)
-        : mRenderThread(thread)
-        , mCaches(nullptr)
-        , mViewportWidth(0)
-        , mViewportHeight(0)
-        , mFramebuffer(0) {
-    mThreadId = pthread_self();
-}
-
-RenderState::~RenderState() {
-}
-
-void RenderState::onGLContextCreated() {
-    // This is delayed because the first access of Caches makes GL calls
-    mCaches = &Caches::getInstance();
-    mCaches->init();
-    mCaches->setRenderState(this);
-    mCaches->textureCache.setAssetAtlas(&mAssetAtlas);
-}
-
-void RenderState::onGLContextDestroyed() {
-/*
-    size_t size = mActiveLayers.size();
-    if (CC_UNLIKELY(size != 0)) {
-        ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d",
-                mRegisteredContexts.size(), size, mActiveLayers.empty());
-        mCaches->dumpMemoryUsage();
-        for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin();
-                cit != mRegisteredContexts.end(); cit++) {
-            renderthread::CanvasContext* context = *cit;
-            ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get());
-            ALOGE("  Prefeteched layers: %zu", context->mPrefetechedLayers.size());
-            for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin();
-                    pit != context->mPrefetechedLayers.end(); pit++) {
-                (*pit)->debugDumpLayers("    ");
-            }
-            context->mRootRenderNode->debugDumpLayers("  ");
-        }
-
-
-        if (mActiveLayers.begin() == mActiveLayers.end()) {
-            ALOGE("set has become empty. wat.");
-        }
-        for (std::set<const Layer*>::iterator lit = mActiveLayers.begin();
-             lit != mActiveLayers.end(); lit++) {
-            const Layer* layer = *(lit);
-            ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d",
-                    layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered);
-        }
-        LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size);
-    }
-*/
-    mAssetAtlas.terminate();
-}
-
-void RenderState::setViewport(GLsizei width, GLsizei height) {
-    mViewportWidth = width;
-    mViewportHeight = height;
-    glViewport(0, 0, mViewportWidth, mViewportHeight);
-}
-
-
-void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
-    *outWidth = mViewportWidth;
-    *outHeight = mViewportHeight;
-}
-
-void RenderState::bindFramebuffer(GLuint fbo) {
-    if (mFramebuffer != fbo) {
-        mFramebuffer = fbo;
-        glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
-    }
-}
-
-void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
-    interruptForFunctorInvoke();
-    (*functor)(mode, info);
-    resumeFromFunctorInvoke();
-}
-
-void RenderState::interruptForFunctorInvoke() {
-    if (mCaches->currentProgram) {
-        if (mCaches->currentProgram->isInUse()) {
-            mCaches->currentProgram->remove();
-            mCaches->currentProgram = nullptr;
-        }
-    }
-    mCaches->resetActiveTexture();
-    mCaches->unbindMeshBuffer();
-    mCaches->unbindIndicesBuffer();
-    mCaches->resetVertexPointers();
-    mCaches->disableTexCoordsVertexArray();
-    debugOverdraw(false, false);
-}
-
-void RenderState::resumeFromFunctorInvoke() {
-    glViewport(0, 0, mViewportWidth, mViewportHeight);
-    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
-    debugOverdraw(false, false);
-
-    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-    mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
-    mCaches->enableScissor();
-    mCaches->resetScissor();
-
-    mCaches->activeTexture(0);
-    mCaches->resetBoundTextures();
-
-    mCaches->blend = true;
-    glEnable(GL_BLEND);
-    glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
-    glBlendEquation(GL_FUNC_ADD);
-}
-
-void RenderState::debugOverdraw(bool enable, bool clear) {
-    if (mCaches->debugOverdraw && mFramebuffer == 0) {
-        if (clear) {
-            mCaches->disableScissor();
-            mCaches->stencil.clear();
-        }
-        if (enable) {
-            mCaches->stencil.enableDebugWrite();
-        } else {
-            mCaches->stencil.disable();
-        }
-    }
-}
-
-void RenderState::requireGLContext() {
-    assertOnGLThread();
-    mRenderThread.eglManager().requireGlContext();
-}
-
-void RenderState::assertOnGLThread() {
-    pthread_t curr = pthread_self();
-    LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!");
-}
-
-
-class DecStrongTask : public renderthread::RenderTask {
-public:
-    DecStrongTask(VirtualLightRefBase* object) : mObject(object) {}
-
-    virtual void run() override {
-        mObject->decStrong(nullptr);
-        mObject = nullptr;
-        delete this;
-    }
-
-private:
-    VirtualLightRefBase* mObject;
-};
-
-void RenderState::postDecStrong(VirtualLightRefBase* object) {
-    mRenderThread.queue(new DecStrongTask(object));
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index c6483ac..4333792 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -81,7 +81,7 @@
     uint32_t mPixelRefStableID;
 
     friend class ResourceCache;
-    friend class android::key_value_pair_t<BitmapKey, SkBitmap*>;
+    friend struct android::key_value_pair_t<BitmapKey, SkBitmap*>;
 };
 
 class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index f8917e3..ca7f48d 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -113,33 +113,6 @@
 #endif
 }
 
-void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
-    int currentIndex = 0;
-    const int rays = SHADOW_RAY_COUNT;
-    // For the penumbra area.
-    for (int layer = 0; layer < 2; layer ++) {
-        int baseIndex = layer * rays;
-        for (int i = 0; i < rays; i++) {
-            shadowIndices[currentIndex++] = i + baseIndex;
-            shadowIndices[currentIndex++] = rays + i + baseIndex;
-        }
-        // To close the loop, back to the ray 0.
-        shadowIndices[currentIndex++] = 0 + baseIndex;
-         // Note this is the same as the first index of next layer loop.
-        shadowIndices[currentIndex++] = rays + baseIndex;
-    }
-
-#if DEBUG_SHADOW
-    if (currentIndex != MAX_SHADOW_INDEX_COUNT) {
-        ALOGW("vertex index count is wrong. current %d, expected %d",
-                currentIndex, MAX_SHADOW_INDEX_COUNT);
-    }
-    for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) {
-        ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]);
-    }
-#endif
-}
-
 /**
  * Calculate the centroid of a 2d polygon.
  *
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index 16ad91d..c04d8ef 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -78,8 +78,6 @@
             const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
             const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer);
 
-    static void generateShadowIndices(uint16_t*  shadowIndices);
-
     static Vector2 centroid2d(const Vector2* poly, int polyLength);
 
     static bool isClockwise(const Vector2* polygon, int len);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 7fdbf9a..efbb709 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -46,82 +46,87 @@
         canvas->ref();
     }
 
-    virtual SkCanvas* getSkCanvas() {
+    virtual SkCanvas* asSkCanvas() override {
         return mCanvas.get();
     }
 
-    virtual void setBitmap(SkBitmap* bitmap, bool copyState);
+    virtual void setBitmap(SkBitmap* bitmap, bool copyState) override;
 
-    virtual bool isOpaque();
-    virtual int width();
-    virtual int height();
+    virtual bool isOpaque() override;
+    virtual int width() override;
+    virtual int height() override;
 
-    virtual int getSaveCount() const;
-    virtual int save(SkCanvas::SaveFlags flags);
-    virtual void restore();
-    virtual void restoreToCount(int saveCount);
+    virtual int getSaveCount() const override;
+    virtual int save(SkCanvas::SaveFlags flags) override;
+    virtual void restore() override;
+    virtual void restoreToCount(int saveCount) override;
 
     virtual int saveLayer(float left, float top, float right, float bottom,
-                const SkPaint* paint, SkCanvas::SaveFlags flags);
+                const SkPaint* paint, SkCanvas::SaveFlags flags) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags);
+            int alpha, SkCanvas::SaveFlags flags) override;
 
-    virtual void getMatrix(SkMatrix* outMatrix) const;
-    virtual void setMatrix(const SkMatrix& matrix);
-    virtual void concat(const SkMatrix& matrix);
-    virtual void rotate(float degrees);
-    virtual void scale(float sx, float sy);
-    virtual void skew(float sx, float sy);
-    virtual void translate(float dx, float dy);
+    virtual void getMatrix(SkMatrix* outMatrix) const override;
+    virtual void setMatrix(const SkMatrix& matrix) override;
+    virtual void concat(const SkMatrix& matrix) override;
+    virtual void rotate(float degrees) override;
+    virtual void scale(float sx, float sy) override;
+    virtual void skew(float sx, float sy) override;
+    virtual void translate(float dx, float dy) override;
 
-    virtual bool getClipBounds(SkRect* outRect) const;
-    virtual bool quickRejectRect(float left, float top, float right, float bottom) const;
-    virtual bool quickRejectPath(const SkPath& path) const;
-    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
-    virtual bool clipPath(const SkPath* path, SkRegion::Op op);
-    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+    virtual bool getClipBounds(SkRect* outRect) const override;
+    virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
+    virtual bool quickRejectPath(const SkPath& path) const override;
+    virtual bool clipRect(float left, float top, float right, float bottom,
+            SkRegion::Op op) override;
+    virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
+    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
 
-    virtual SkDrawFilter* getDrawFilter();
-    virtual void setDrawFilter(SkDrawFilter* drawFilter);
+    virtual SkDrawFilter* getDrawFilter() override;
+    virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
 
-    virtual void drawColor(int color, SkXfermode::Mode mode);
-    virtual void drawPaint(const SkPaint& paint);
+    virtual void drawColor(int color, SkXfermode::Mode mode) override;
+    virtual void drawPaint(const SkPaint& paint) override;
 
-    virtual void drawPoint(float x, float y, const SkPaint& paint);
-    virtual void drawPoints(const float* points, int count, const SkPaint& paint);
+    virtual void drawPoint(float x, float y, const SkPaint& paint) override;
+    virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
     virtual void drawLine(float startX, float startY, float stopX, float stopY,
-            const SkPaint& paint);
-    virtual void drawLines(const float* points, int count, const SkPaint& paint);
-    virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint);
+            const SkPaint& paint) override;
+    virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+    virtual void drawRect(float left, float top, float right, float bottom,
+            const SkPaint& paint) override;
     virtual void drawRoundRect(float left, float top, float right, float bottom,
-            float rx, float ry, const SkPaint& paint);
-    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint);
-    virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint);
+            float rx, float ry, const SkPaint& paint) override;
+    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+    virtual void drawOval(float left, float top, float right, float bottom,
+            const SkPaint& paint) override;
     virtual void drawArc(float left, float top, float right, float bottom,
-            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint);
-    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+    virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
     virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
             const float* verts, const float* tex, const int* colors,
-            const uint16_t* indices, int indexCount, const SkPaint& paint);
+            const uint16_t* indices, int indexCount, const SkPaint& paint) override;
 
-    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint);
-    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint) override;
     virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, const SkPaint* paint);
+            float dstRight, float dstBottom, const SkPaint* paint) override;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
-            const float* vertices, const int* colors, const SkPaint* paint);
+            const float* vertices, const int* colors, const SkPaint* paint) override;
 
     virtual void drawText(const uint16_t* text, const float* positions, int count,
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
-            float totalAdvance);
+            float totalAdvance) override;
     virtual void drawPosText(const uint16_t* text, const float* positions, int count,
-            int posCount, const SkPaint& paint);
+            int posCount, const SkPaint& paint) override;
     virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint);
+            float hOffset, float vOffset, const SkPaint& paint) override;
 
-    virtual bool drawTextAbsolutePos() const { return true; }
+    virtual bool drawTextAbsolutePos() const  override { return true; }
 
 private:
     struct SaveRec {
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
new file mode 100644
index 0000000..de5f91c
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+#include "SkiaCanvasProxy.h"
+
+#include <cutils/log.h>
+#include <SkPatchUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas)
+        : INHERITED(canvas->width(), canvas->height())
+        , mCanvas(canvas) {}
+
+void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
+    mCanvas->drawPaint(paint);
+}
+
+void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
+        const SkPaint& paint) {
+    // convert the SkPoints into floats
+    SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+    const size_t floatCount = count << 1;
+    const float* floatArray = &pts[0].fX;
+
+    switch (pointMode) {
+        case kPoints_PointMode: {
+            mCanvas->drawPoints(floatArray, floatCount, paint);
+            break;
+        }
+        case kLines_PointMode: {
+            mCanvas->drawLines(floatArray, floatCount, paint);
+            break;
+        }
+        case kPolygon_PointMode: {
+            SkPaint strokedPaint(paint);
+            strokedPaint.setStyle(SkPaint::kStroke_Style);
+
+            SkPath path;
+            for (size_t i = 0; i < count - 1; i++) {
+                path.moveTo(pts[i]);
+                path.lineTo(pts[i+1]);
+                this->drawPath(path, strokedPaint);
+                path.rewind();
+            }
+            break;
+        }
+        default:
+            LOG_ALWAYS_FATAL("Unknown point type");
+    }
+}
+
+void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
+    mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+    mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
+    if (!roundRect.isComplex()) {
+        const SkRect& rect = roundRect.rect();
+        SkVector radii = roundRect.getSimpleRadii();
+        mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+                               radii.fX, radii.fY, paint);
+    } else {
+        SkPath path;
+        path.addRRect(roundRect);
+        mCanvas->drawPath(path, paint);
+    }
+}
+
+void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
+    mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+        const SkPaint* paint) {
+    mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+        const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+    SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+    mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+                        dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+        const SkRect& dst, const SkPaint*) {
+    //TODO make nine-patch drawing a method on Canvas.h
+    SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
+}
+
+void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top,
+        const SkPaint* paint) {
+    mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+    mCanvas->setMatrix(SkMatrix::I());
+    mCanvas->drawBitmap(bitmap, left, top, paint);
+    mCanvas->restore();
+}
+
+void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[],
+        const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[],
+        int indexCount, const SkPaint& paint) {
+    // convert the SkPoints into floats
+    SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+    const int floatCount = vertexCount << 1;
+    const float* vArray = &vertices[0].fX;
+    const float* tArray = (texs) ? &texs[0].fX : NULL;
+    const int* cArray = (colors) ? (int*)colors : NULL;
+    mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint);
+}
+
+SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+    SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
+    return NULL;
+}
+
+void SkiaCanvasProxy::willSave() {
+    mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+}
+
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
+        const SkPaint* paint, SaveFlags flags) {
+    SkRect rect;
+    if (rectPtr) {
+        rect = *rectPtr;
+    } else if(!mCanvas->getClipBounds(&rect)) {
+        rect = SkRect::MakeEmpty();
+    }
+    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+    return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkiaCanvasProxy::willRestore() {
+    mCanvas->restore();
+}
+
+void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
+    mCanvas->concat(matrix);
+}
+
+void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
+    mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+        const SkPaint& paint) {
+    SkPath path;
+    path.addRRect(outer);
+    path.addRRect(inner);
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    this->drawPath(path, paint);
+}
+
+/**
+ * Utility class that converts the incoming text & paint from the given encoding
+ * into glyphIDs.
+ */
+class GlyphIDConverter {
+public:
+    GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
+        paint = origPaint;
+        if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
+            glyphIDs = (uint16_t*)text;
+            count = byteLength >> 1;
+        } else {
+            storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding.
+            glyphIDs = storage.get();
+            count = paint.textToGlyphs(text, byteLength, storage.get());
+            paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        }
+    }
+
+    SkPaint paint;
+    uint16_t* glyphIDs;
+    int count;
+private:
+    SkAutoSTMalloc<32, uint16_t> storage;
+};
+
+void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+        const SkPaint& origPaint) {
+    // convert to glyphIDs if necessary
+    GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+    // compute the glyph positions
+    SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count);
+    SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count);
+    glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
+
+    // compute conservative bounds
+    // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+    //       but even more conservative bounds if this  is too slow.
+    SkRect bounds;
+    glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+    // adjust for non-left alignment
+    if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkScalar stop = 0;
+        for (int i = 0; i < glyphs.count; i++) {
+            stop += glyphWidths[i];
+        }
+        if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
+            stop = SkScalarHalf(stop);
+        }
+        if (glyphs.paint.isVerticalText()) {
+            y -= stop;
+        } else {
+            x -= stop;
+        }
+    }
+
+    // setup the first glyph position and adjust bounds if needed
+    if (mCanvas->drawTextAbsolutePos()) {
+        bounds.offset(x,y);
+        pointStorage[0].set(x, y);
+    } else {
+        pointStorage[0].set(0, 0);
+    }
+
+    // setup the remaining glyph positions
+    if (glyphs.paint.isVerticalText()) {
+        for (int i = 1; i < glyphs.count; i++) {
+            pointStorage[i].set(x, glyphWidths[i-1] + pointStorage[i-1].fY);
+        }
+    } else {
+        for (int i = 1; i < glyphs.count; i++) {
+            pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, y);
+        }
+    }
+
+    SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+    mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
+                      x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+        const SkPaint& origPaint) {
+    // convert to glyphIDs if necessary
+    GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+    // convert to relative positions if necessary
+    int x, y;
+    const SkPoint* posArray;
+    SkAutoSTMalloc<32, SkPoint> pointStorage;
+    if (mCanvas->drawTextAbsolutePos()) {
+        x = 0;
+        y = 0;
+        posArray = pos;
+    } else {
+        x = pos[0].fX;
+        y = pos[0].fY;
+        posArray = pointStorage.reset(glyphs.count);
+        for (int i = 0; i < glyphs.count; i++) {
+            pointStorage[i].fX = pos[i].fX- x;
+            pointStorage[i].fY = pos[i].fY- y;
+        }
+    }
+
+    // compute conservative bounds
+    // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+    //       but even more conservative bounds if this  is too slow.
+    SkRect bounds;
+    glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+    SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+    mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
+                      bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+        SkScalar constY, const SkPaint& paint) {
+    const size_t pointCount = byteLength >> 1;
+    SkAutoSTMalloc<32, SkPoint> storage(pointCount);
+    SkPoint* pts = storage.get();
+    for (size_t i = 0; i < pointCount; i++) {
+        pts[i].set(xpos[i], constY);
+    }
+    this->onDrawPosText(text, byteLength, pts, paint);
+}
+
+void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+        const SkMatrix* matrix, const SkPaint& origPaint) {
+    // convert to glyphIDs if necessary
+    GlyphIDConverter glyphs(text, byteLength, origPaint);
+    mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+}
+
+void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+        const SkPaint& paint) {
+    SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+}
+
+void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+        const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
+    SkPatchUtils::VertexData data;
+
+    SkMatrix matrix;
+    mCanvas->getMatrix(&matrix);
+    SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
+
+    // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
+    // If it fails to generate the vertices, then we do not draw.
+    if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
+        this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
+                           data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
+                           paint);
+    }
+}
+
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+    mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
+}
+
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+    SkPath path;
+    path.addRRect(roundRect);
+    mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+    mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+    mCanvas->clipRegion(&region, op);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
new file mode 100644
index 0000000..4322fcf
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef SkiaCanvasProxy_DEFINED
+#define SkiaCanvasProxy_DEFINED
+
+#include <cutils/compiler.h>
+#include <SkCanvas.h>
+
+#include "Canvas.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class serves as a proxy between Skia's SkCanvas and Android Framework's
+ * Canvas.  The class does not maintain any state and will pass through any request
+ * directly to the Canvas provided in the constructor.
+ *
+ * Upon construction it is expected that the provided Canvas has already been
+ * prepared for recording and will continue to be in the recording state while
+ * this proxy class is being used.
+ */
+class ANDROID_API SkiaCanvasProxy : public SkCanvas {
+public:
+    SkiaCanvasProxy(Canvas* canvas);
+    virtual ~SkiaCanvasProxy() {}
+
+protected:
+
+    virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+
+    virtual void willSave() override;
+    virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    virtual void willRestore() override;
+
+    virtual void didConcat(const SkMatrix&) override;
+    virtual void didSetMatrix(const SkMatrix&) override;
+
+    virtual void onDrawPaint(const SkPaint& paint) override;
+    virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
+                              const SkPaint&) override;
+    virtual void onDrawOval(const SkRect&, const SkPaint&) override;
+    virtual void onDrawRect(const SkRect&, const SkPaint&) override;
+    virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
+    virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                              const SkPaint*) override;
+    virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
+                                  const SkPaint* paint, DrawBitmapRectFlags flags) override;
+    virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                  const SkRect& dst, const SkPaint*) override;
+    virtual void onDrawSprite(const SkBitmap&, int left, int top,
+                              const SkPaint*) override;
+    virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[],
+                                const SkPoint texs[], const SkColor colors[], SkXfermode*,
+                                const uint16_t indices[], int indexCount,
+                                const SkPaint&) override;
+
+    virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+
+    virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                            const SkPaint&) override;
+    virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                               const SkPaint&) override;
+    virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+                                SkScalar constY, const SkPaint&) override;
+    virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                                  const SkMatrix* matrix, const SkPaint&) override;
+    virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                                const SkPaint& paint) override;
+
+    virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                             const SkPoint texCoords[4], SkXfermode* xmode,
+                             const SkPaint& paint) override;
+
+    virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    virtual void onClipRegion(const SkRegion&, SkRegion::Op) override;
+
+private:
+    Canvas* mCanvas;
+
+    typedef SkCanvas INHERITED;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 2c09344..9c929da 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -57,7 +57,7 @@
 }
 
 static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
-    caches->bindTexture(texture->id);
+    caches->textureState().bindTexture(texture->id);
     texture->setWrapST(wrapS, wrapT);
 }
 
@@ -176,7 +176,7 @@
     }
 
     GLuint textureSlot = (*textureUnit)++;
-    caches->activeTexture(textureSlot);
+    caches->textureState().activateTexture(textureSlot);
 
     const float width = layer->getWidth();
     const float height = layer->getHeight();
@@ -191,11 +191,11 @@
     layer->setWrap(GL_CLAMP_TO_EDGE);
     layer->setFilter(GL_LINEAR);
 
-    Program* program = caches->currentProgram;
-    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
-    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+    Program& program = caches->program();
+    glUniform1i(program.getUniform("bitmapSampler"), textureSlot);
+    glUniformMatrix4fv(program.getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
-    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+    glUniform2f(program.getUniform("textureDimension"), 1.0f / width, 1.0f / height);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -270,14 +270,14 @@
     }
 
     GLuint textureSlot = (*textureUnit)++;
-    Caches::getInstance().activeTexture(textureSlot);
+    Caches::getInstance().textureState().activateTexture(textureSlot);
 
     BitmapShaderInfo shaderInfo;
     if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) {
         return;
     }
 
-    Program* program = caches->currentProgram;
+    Program& program = caches->program();
     Texture* texture = shaderInfo.texture;
 
     const AutoTexture autoCleanup(texture);
@@ -290,10 +290,10 @@
     bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
     texture->setFilter(GL_LINEAR);
 
-    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
-    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+    glUniform1i(program.getUniform("bitmapSampler"), textureSlot);
+    glUniformMatrix4fv(program.getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
-    glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
+    glUniform2f(program.getUniform("textureDimension"), 1.0f / shaderInfo.width,
             1.0f / shaderInfo.height);
 }
 
@@ -381,7 +381,7 @@
 
     SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
 
-    Program* program = caches->currentProgram;
+    Program& program = caches->program();
     if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
         if (gradInfo.fColorCount > COLOR_COUNT) {
             // There was not enough room in our arrays for all the colors and offsets. Try again,
@@ -392,7 +392,7 @@
             shader.asAGradient(&gradInfo);
         }
         GLuint textureSlot = (*textureUnit)++;
-        caches->activeTexture(textureSlot);
+        caches->textureState().activateTexture(textureSlot);
 
 #ifndef SK_SCALAR_IS_FLOAT
     #error Need to convert gradInfo.fColorOffsets to float!
@@ -402,10 +402,10 @@
 
         // Uniforms
         bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
-        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+        glUniform1i(program.getUniform("gradientSampler"), textureSlot);
     } else {
-        bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
-        bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
+        bindUniformColor(program.getUniform("startColor"), gradInfo.fColors[0]);
+        bindUniformColor(program.getUniform("endColor"), gradInfo.fColors[1]);
     }
 
     caches->dither.setupProgram(program, textureUnit);
@@ -428,7 +428,7 @@
 
     mat4 screenSpace;
     computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
-    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+    glUniformMatrix4fv(program.getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 49fb4ba..597d95c 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -203,8 +203,8 @@
     ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
             this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
     const Rect& clipRect(mClipArea->getClipRect());
-    ALOGD("  ClipRect (at %p) %.1f %.1f %.1f %.1f",
-            clipRect, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+    ALOGD("  ClipRect %.1f %.1f %.1f %.1f",
+            clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
     ALOGD("  Transform (at %p):", transform);
     transform->dump();
 }
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index a03192a..b3b06d6 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -1031,7 +1031,7 @@
     ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Spot Vertex Buffer");
     ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Spot Index Buffer");
 
-    shadowTriangleStrip.setMode(VertexBuffer::kIndices);
+    shadowTriangleStrip.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices);
     shadowTriangleStrip.computeBounds<AlphaVertex>();
 }
 
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 4f028d5..66de333 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -385,7 +385,9 @@
     if (mShadowProcessor == nullptr) {
         mShadowProcessor = new ShadowProcessor(Caches::getInstance());
     }
-    mShadowProcessor->add(task);
+    if (!mShadowProcessor->add(task)) {
+        mShadowProcessor->process(task);
+    }
 
     task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache
     mShadowCache.put(key, task.get());
@@ -421,7 +423,9 @@
         if (mProcessor == nullptr) {
             mProcessor = new TessellationProcessor(Caches::getInstance());
         }
-        mProcessor->add(task);
+        if (!mProcessor->add(task)) {
+            mProcessor->process(task);
+        }
         mCache.put(entry, buffer);
     }
     return buffer;
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 4ec298d..c2e88f3 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -207,7 +207,7 @@
 
         glGenTextures(1, &texture->id);
 
-        caches.bindTexture(texture->id);
+        caches.textureState().bindTexture(texture->id);
         // Textures are Alpha8
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 58fd972..512f5cf 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -24,18 +24,44 @@
 namespace android {
 namespace uirenderer {
 
-Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
-        cleanup(false), bitmapSize(0), mipMap(false), uvMapper(nullptr), isInUse(false),
-        mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
-        mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
-        mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
+Texture::Texture()
+        : id(0)
+        , generation(0)
+        , blend(false)
+        , width(0)
+        , height(0)
+        , cleanup(false)
+        , bitmapSize(0)
+        , mipMap(false)
+        , uvMapper(nullptr)
+        , isInUse(false)
+        , mWrapS(GL_CLAMP_TO_EDGE)
+        , mWrapT(GL_CLAMP_TO_EDGE)
+        , mMinFilter(GL_NEAREST)
+        , mMagFilter(GL_NEAREST)
+        , mFirstFilter(true)
+        , mFirstWrap(true)
+        , mCaches(Caches::getInstance()) {
 }
 
-Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
-        cleanup(false), bitmapSize(0), mipMap(false), uvMapper(nullptr), isInUse(false),
-        mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
-        mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
-        mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
+Texture::Texture(Caches& caches)
+        : id(0)
+        , generation(0)
+        , blend(false)
+        , width(0)
+        , height(0)
+        , cleanup(false)
+        , bitmapSize(0)
+        , mipMap(false)
+        , uvMapper(nullptr)
+        , isInUse(false)
+        , mWrapS(GL_CLAMP_TO_EDGE)
+        , mWrapT(GL_CLAMP_TO_EDGE)
+        , mMinFilter(GL_NEAREST)
+        , mMagFilter(GL_NEAREST)
+        , mFirstFilter(true)
+        , mFirstWrap(true)
+        , mCaches(caches) {
 }
 
 void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
@@ -48,7 +74,7 @@
         mWrapT = wrapT;
 
         if (bindTexture) {
-            mCaches.bindTexture(renderTarget, id);
+            mCaches.textureState().bindTexture(renderTarget, id);
         }
 
         glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
@@ -66,7 +92,7 @@
         mMagFilter = mag;
 
         if (bindTexture) {
-            mCaches.bindTexture(renderTarget, id);
+            mCaches.textureState().bindTexture(renderTarget, id);
         }
 
         if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
@@ -77,7 +103,7 @@
 }
 
 void Texture::deleteTexture() const {
-    mCaches.deleteTexture(id);
+    mCaches.textureState().deleteTexture(id);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 524f206..f4f8e44 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -38,10 +38,12 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-TextureCache::TextureCache():
-        mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity),
-        mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
-        mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE), mAssetAtlas(nullptr) {
+TextureCache::TextureCache()
+        : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity)
+        , mSize(0)
+        , mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE))
+        , mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE)
+        , mAssetAtlas(nullptr) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, nullptr) > 0) {
         INIT_LOGD("  Setting texture cache size to %sMB", property);
@@ -59,20 +61,6 @@
                 DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f);
     }
 
-    init();
-}
-
-TextureCache::TextureCache(uint32_t maxByteSize):
-        mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity),
-        mSize(0), mMaxSize(maxByteSize), mAssetAtlas(nullptr) {
-    init();
-}
-
-TextureCache::~TextureCache() {
-    mCache.clear();
-}
-
-void TextureCache::init() {
     mCache.setOnEntryRemovedListener(this);
 
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
@@ -81,6 +69,10 @@
     mDebugEnabled = readDebugLevel() & kDebugCaches;
 }
 
+TextureCache::~TextureCache() {
+    mCache.clear();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Size management
 ///////////////////////////////////////////////////////////////////////////////
@@ -280,7 +272,7 @@
 
     // We could also enable mipmapping if both bitmap dimensions are powers
     // of 2 but we'd have to deal with size changes. Let's keep this simple
-    const bool canMipMap = Extensions::getInstance().hasNPot();
+    const bool canMipMap = Caches::getInstance().extensions().hasNPot();
 
     // If the texture had mipmap enabled but not anymore,
     // force a glTexImage2D to discard the mipmap levels
@@ -296,7 +288,7 @@
     texture->width = bitmap->width();
     texture->height = bitmap->height();
 
-    Caches::getInstance().bindTexture(texture->id);
+    Caches::getInstance().textureState().bindTexture(texture->id);
 
     switch (bitmap->colorType()) {
     case kAlpha_8_SkColorType:
@@ -355,7 +347,8 @@
 void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
         GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
     glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
-    const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
+    const bool useStride = stride != width
+            && Caches::getInstance().extensions().hasUnpackRowLength();
     if ((stride == width) || useStride) {
         if (useStride) {
             glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index b97d92d..a2c6380 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -52,10 +52,9 @@
  * Any texture added to the cache causing the cache to grow beyond the maximum
  * allowed size will also cause the oldest texture to be kicked out.
  */
-class TextureCache: public OnEntryRemoved<uint32_t, Texture*> {
+class TextureCache : public OnEntryRemoved<uint32_t, Texture*> {
 public:
     TextureCache();
-    TextureCache(uint32_t maxByteSize);
     ~TextureCache();
 
     /**
@@ -146,8 +145,6 @@
     void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
             GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
 
-    void init();
-
     LruCache<uint32_t, Texture*> mCache;
 
     uint32_t mSize;
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index aa6acc9..7c3f2fd 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -17,6 +17,9 @@
 #ifndef ANDROID_HWUI_VECTOR_H
 #define ANDROID_HWUI_VECTOR_H
 
+#include <math.h>
+#include <utils/Log.h>
+
 namespace android {
 namespace uirenderer {
 
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index d81dd42..9be4d84 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -24,11 +24,10 @@
 
 class VertexBuffer {
 public:
-    enum Mode {
-        kStandard = 0,
-        kOnePolyRingShadow = 1,
-        kTwoPolyRingShadow = 2,
-        kIndices = 3
+    enum MeshFeatureFlags {
+        kNone = 0,
+        kAlpha = 1 << 0,
+        kIndices = 1 << 1,
     };
 
     VertexBuffer()
@@ -39,7 +38,7 @@
             , mAllocatedVertexCount(0)
             , mAllocatedIndexCount(0)
             , mByteCount(0)
-            , mMode(kStandard)
+            , mMeshFeatureFlags(kNone)
             , mReallocBuffer(nullptr)
             , mCleanupMethod(nullptr)
             , mCleanupIndexMethod(nullptr)
@@ -135,10 +134,12 @@
     void updateVertexCount(unsigned int newCount)  {
         mVertexCount = MathUtils::min(newCount, mAllocatedVertexCount);
     }
-    Mode getMode() const { return mMode; }
+    MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; }
+    void setMeshFeatureFlags(int flags) {
+        mMeshFeatureFlags = static_cast<MeshFeatureFlags>(flags);
+    }
 
     void setBounds(Rect bounds) { mBounds = bounds; }
-    void setMode(Mode mode) { mMode = mode; }
 
     template <class TYPE>
     void createDegenerateSeparators(int allocSize) {
@@ -166,7 +167,7 @@
     unsigned int mAllocatedIndexCount;
     unsigned int mByteCount;
 
-    Mode mMode;
+    MeshFeatureFlags mMeshFeatureFlags;
 
     void* mReallocBuffer; // used for multi-allocation
 
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 128e392..9314126 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -120,7 +120,7 @@
     // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
     // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
     // With OpenGL ES 2.0 we have to upload entire stripes instead.
-    mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength();
+    mHasUnpackRowLength = mCaches.extensions().hasUnpackRowLength();
 }
 
 CacheTexture::~CacheTexture() {
@@ -157,7 +157,7 @@
         mTexture = nullptr;
     }
     if (mTextureId) {
-        mCaches.deleteTexture(mTextureId);
+        mCaches.textureState().deleteTexture(mTextureId);
         mTextureId = 0;
     }
     mDirty = false;
@@ -169,7 +169,7 @@
        mLinearFiltering = linearFiltering;
 
        const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
-       if (bind) mCaches.bindTexture(getTextureId());
+       if (bind) mCaches.textureState().bindTexture(getTextureId());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
    }
@@ -189,7 +189,7 @@
     if (!mTextureId) {
         glGenTextures(1, &mTextureId);
 
-        mCaches.bindTexture(mTextureId);
+        mCaches.textureState().bindTexture(mTextureId);
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
         // Initialize texture dimensions
         glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
new file mode 100644
index 0000000..c751dba
--- /dev/null
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+#include <renderstate/Blend.h>
+#include "Program.h"
+
+#include "ShadowTessellator.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure mapping Skia xfermodes to OpenGL blending factors.
+ */
+struct Blender {
+    SkXfermode::Mode mode;
+    GLenum src;
+    GLenum dst;
+};
+
+// In this array, the index of each Blender equals the value of the first
+// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+const Blender kBlends[] = {
+    { SkXfermode::kClear_Mode,    GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kSrc_Mode,      GL_ONE,                 GL_ZERO },
+    { SkXfermode::kDst_Mode,      GL_ZERO,                GL_ONE },
+    { SkXfermode::kSrcOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kDstOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+    { SkXfermode::kSrcIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
+    { SkXfermode::kDstIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
+    { SkXfermode::kSrcOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+    { SkXfermode::kDstOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kSrcATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kDstATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
+    { SkXfermode::kModulate_Mode, GL_ZERO,                GL_SRC_COLOR },
+    { SkXfermode::kScreen_Mode,   GL_ONE,                 GL_ONE_MINUS_SRC_COLOR }
+};
+
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
+const Blender kBlendsSwap[] = {
+    { SkXfermode::kClear_Mode,    GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+    { SkXfermode::kSrc_Mode,      GL_ZERO,                GL_ONE },
+    { SkXfermode::kDst_Mode,      GL_ONE,                 GL_ZERO },
+    { SkXfermode::kSrcOver_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+    { SkXfermode::kDstOver_Mode,  GL_ONE,                 GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kSrcIn_Mode,    GL_ZERO,                GL_SRC_ALPHA },
+    { SkXfermode::kDstIn_Mode,    GL_DST_ALPHA,           GL_ZERO },
+    { SkXfermode::kSrcOut_Mode,   GL_ZERO,                GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kDstOut_Mode,   GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+    { SkXfermode::kSrcATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+    { SkXfermode::kDstATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+    { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
+    { SkXfermode::kModulate_Mode, GL_DST_COLOR,           GL_ZERO },
+    { SkXfermode::kScreen_Mode,   GL_ONE_MINUS_DST_COLOR, GL_ONE }
+};
+
+Blend::Blend()
+    : mEnabled(false)
+    , mSrcMode(GL_ZERO)
+    , mDstMode(GL_ZERO) {
+    // gl blending off by default
+}
+
+void Blend::enable(SkXfermode::Mode mode, bool swapSrcDst) {
+    GLenum srcMode;
+    GLenum dstMode;
+    getFactors(mode, swapSrcDst, &srcMode, &dstMode);
+    setFactors(srcMode, dstMode);
+}
+
+void Blend::disable() {
+    if (mEnabled) {
+        glDisable(GL_BLEND);
+        mEnabled = false;
+    }
+}
+
+void Blend::invalidate() {
+    syncEnabled();
+    mSrcMode = mDstMode = GL_ZERO;
+}
+
+void Blend::syncEnabled() {
+    if (mEnabled) {
+        glEnable(GL_BLEND);
+    } else {
+        glDisable(GL_BLEND);
+    }
+}
+
+void Blend::getFactors(SkXfermode::Mode mode, bool swapSrcDst, GLenum* outSrc, GLenum* outDst) {
+    *outSrc = swapSrcDst ? kBlendsSwap[mode].src : kBlends[mode].src;
+    *outDst = swapSrcDst ? kBlendsSwap[mode].dst : kBlends[mode].dst;
+}
+
+void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
+    if (srcMode == GL_ZERO && dstMode == GL_ZERO) {
+        disable();
+    } else {
+        if (!mEnabled) {
+            glEnable(GL_BLEND);
+            mEnabled = true;
+        }
+
+        if (srcMode != mSrcMode || dstMode != mSrcMode) {
+            glBlendFunc(srcMode, dstMode);
+            mSrcMode = srcMode;
+            mDstMode = dstMode;
+        }
+    }
+}
+
+void Blend::dump() {
+    ALOGD("Blend: enabled %d, func src %d, dst %d", mEnabled, mSrcMode, mDstMode);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
new file mode 100644
index 0000000..6d0c115c
--- /dev/null
+++ b/libs/hwui/renderstate/Blend.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_BLEND_H
+#define RENDERSTATE_BLEND_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class Blend {
+    friend class RenderState;
+public:
+    void enable(SkXfermode::Mode mode, bool swapSrcDst);
+    void disable();
+    void syncEnabled();
+
+    static void getFactors(SkXfermode::Mode mode, bool swapSrcDst, GLenum* outSrc, GLenum* outDst);
+    void setFactors(GLenum src, GLenum dst);
+
+    void dump();
+private:
+    Blend();
+    void invalidate();
+    bool mEnabled;
+    GLenum mSrcMode;
+    GLenum mDstMode;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
new file mode 100644
index 0000000..ce6030d
--- /dev/null
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+#include "renderstate/MeshState.h"
+
+#include "Program.h"
+
+#include "ShadowTessellator.h"
+
+namespace android {
+namespace uirenderer {
+
+MeshState::MeshState()
+        : mCurrentIndicesBuffer(0)
+        , mCurrentPixelBuffer(0)
+        , mCurrentPositionPointer(this)
+        , mCurrentPositionStride(0)
+        , mCurrentTexCoordsPointer(this)
+        , mCurrentTexCoordsStride(0)
+        , mTexCoordsArrayEnabled(false)
+        , mQuadListIndices(0) {
+
+    glGenBuffers(1, &mUnitQuadBuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, mUnitQuadBuffer);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(kUnitQuadVertices), kUnitQuadVertices, GL_STATIC_DRAW);
+
+    mCurrentBuffer = mUnitQuadBuffer;
+
+    // position attribute always enabled
+    glEnableVertexAttribArray(Program::kBindingPosition);
+}
+
+MeshState::~MeshState() {
+    glDeleteBuffers(1, &mUnitQuadBuffer);
+    mCurrentBuffer = 0;
+
+    glDeleteBuffers(1, &mQuadListIndices);
+    mQuadListIndices = 0;
+}
+
+void MeshState::dump() {
+    ALOGD("MeshState vertices: unitQuad %d, current %d", mUnitQuadBuffer, mCurrentBuffer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Buffer Objects
+///////////////////////////////////////////////////////////////////////////////
+
+bool MeshState::bindMeshBuffer() {
+    return bindMeshBuffer(mUnitQuadBuffer);
+}
+
+bool MeshState::bindMeshBuffer(GLuint buffer) {
+    if (!buffer) buffer = mUnitQuadBuffer;
+    return bindMeshBufferInternal(buffer);
+}
+
+bool MeshState::unbindMeshBuffer() {
+    return bindMeshBufferInternal(0);
+}
+
+bool MeshState::bindMeshBufferInternal(GLuint buffer) {
+    if (mCurrentBuffer != buffer) {
+        glBindBuffer(GL_ARRAY_BUFFER, buffer);
+        mCurrentBuffer = buffer;
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertices
+///////////////////////////////////////////////////////////////////////////////
+
+void MeshState::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
+    if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
+        glVertexAttribPointer(Program::kBindingPosition, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+        mCurrentPositionPointer = vertices;
+        mCurrentPositionStride = stride;
+    }
+}
+
+void MeshState::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
+    if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
+        glVertexAttribPointer(Program::kBindingTexCoords, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+        mCurrentTexCoordsPointer = vertices;
+        mCurrentTexCoordsStride = stride;
+    }
+}
+
+void MeshState::resetVertexPointers() {
+    mCurrentPositionPointer = this;
+    mCurrentTexCoordsPointer = this;
+}
+
+void MeshState::resetTexCoordsVertexPointer() {
+    mCurrentTexCoordsPointer = this;
+}
+
+void MeshState::enableTexCoordsVertexArray() {
+    if (!mTexCoordsArrayEnabled) {
+        glEnableVertexAttribArray(Program::kBindingTexCoords);
+        mCurrentTexCoordsPointer = this;
+        mTexCoordsArrayEnabled = true;
+    }
+}
+
+void MeshState::disableTexCoordsVertexArray() {
+    if (mTexCoordsArrayEnabled) {
+        glDisableVertexAttribArray(Program::kBindingTexCoords);
+        mTexCoordsArrayEnabled = false;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Indices
+///////////////////////////////////////////////////////////////////////////////
+
+bool MeshState::bindIndicesBufferInternal(const GLuint buffer) {
+    if (mCurrentIndicesBuffer != buffer) {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
+        mCurrentIndicesBuffer = buffer;
+        return true;
+    }
+    return false;
+}
+
+bool MeshState::bindQuadIndicesBuffer() {
+    if (!mQuadListIndices) {
+        std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[kMaxNumberOfQuads * 6]);
+        for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) {
+            uint16_t quad = i * 4;
+            int index = i * 6;
+            regionIndices[index    ] = quad;       // top-left
+            regionIndices[index + 1] = quad + 1;   // top-right
+            regionIndices[index + 2] = quad + 2;   // bottom-left
+            regionIndices[index + 3] = quad + 2;   // bottom-left
+            regionIndices[index + 4] = quad + 1;   // top-right
+            regionIndices[index + 5] = quad + 3;   // bottom-right
+        }
+
+        glGenBuffers(1, &mQuadListIndices);
+        bool force = bindIndicesBufferInternal(mQuadListIndices);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxNumberOfQuads * 6 * sizeof(uint16_t),
+                regionIndices.get(), GL_STATIC_DRAW);
+        return force;
+    }
+
+    return bindIndicesBufferInternal(mQuadListIndices);
+}
+
+bool MeshState::unbindIndicesBuffer() {
+    if (mCurrentIndicesBuffer) {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+        mCurrentIndicesBuffer = 0;
+        return true;
+    }
+    return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
new file mode 100644
index 0000000..afc6267
--- /dev/null
+++ b/libs/hwui/renderstate/MeshState.h
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_MESHSTATE_H
+#define RENDERSTATE_MESHSTATE_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class Program;
+
+// Maximum number of quads that pre-allocated meshes can draw
+const uint32_t kMaxNumberOfQuads = 2048;
+
+// This array is never used directly but used as a memcpy source in the
+// OpenGLRenderer constructor
+const TextureVertex kUnitQuadVertices[] = {
+        { 0, 0, 0, 0 },
+        { 1, 0, 1, 0 },
+        { 0, 1, 0, 1 },
+        { 1, 1, 1, 1 },
+};
+
+const GLsizei kVertexStride = sizeof(Vertex);
+const GLsizei kAlphaVertexStride = sizeof(AlphaVertex);
+const GLsizei kTextureVertexStride = sizeof(TextureVertex);
+
+const GLsizei kMeshTextureOffset = 2 * sizeof(float);
+const GLsizei kVertexAlphaOffset = 2 * sizeof(float);
+const GLsizei kVertexAAWidthOffset = 2 * sizeof(float);
+const GLsizei kVertexAALengthOffset = 3 * sizeof(float);
+const GLsizei kUnitQuadCount = 4;
+
+class MeshState {
+private:
+    friend class RenderState;
+
+public:
+    ~MeshState();
+    void dump();
+    ///////////////////////////////////////////////////////////////////////////////
+    // Buffer objects
+    ///////////////////////////////////////////////////////////////////////////////
+    /**
+     * Binds the VBO used to render simple textured quads.
+     */
+    bool bindMeshBuffer();
+
+    /**
+     * Binds the specified VBO if needed. If buffer == 0, binds default simple textured quad.
+     */
+    bool bindMeshBuffer(GLuint buffer);
+
+    /**
+     * Unbinds the VBO used to render simple textured quads.
+     */
+    bool unbindMeshBuffer();
+
+    ///////////////////////////////////////////////////////////////////////////////
+    // Vertices
+    ///////////////////////////////////////////////////////////////////////////////
+    /**
+     * Binds an attrib to the specified float vertex pointer.
+     * Assumes a stride of gTextureVertexStride and a size of 2.
+     */
+    void bindPositionVertexPointer(bool force, const GLvoid* vertices,
+            GLsizei stride = kTextureVertexStride);
+
+    /**
+     * Binds an attrib to the specified float vertex pointer.
+     * Assumes a stride of gTextureVertexStride and a size of 2.
+     */
+    void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices,
+            GLsizei stride = kTextureVertexStride);
+
+    /**
+     * Resets the vertex pointers.
+     */
+    void resetVertexPointers();
+    void resetTexCoordsVertexPointer();
+
+    void enableTexCoordsVertexArray();
+    void disableTexCoordsVertexArray();
+
+    ///////////////////////////////////////////////////////////////////////////////
+    // Indices
+    ///////////////////////////////////////////////////////////////////////////////
+    /**
+     * Binds a global indices buffer that can draw up to
+     * gMaxNumberOfQuads quads.
+     */
+    bool bindQuadIndicesBuffer();
+    bool unbindIndicesBuffer();
+
+    ///////////////////////////////////////////////////////////////////////////////
+    // Getters - for use in Glop building
+    ///////////////////////////////////////////////////////////////////////////////
+    GLuint getUnitQuadVBO() { return mUnitQuadBuffer; }
+private:
+    MeshState();
+    bool bindMeshBufferInternal(const GLuint buffer);
+    bool bindIndicesBufferInternal(const GLuint buffer);
+
+    GLuint mUnitQuadBuffer;
+
+    GLuint mCurrentBuffer;
+    GLuint mCurrentIndicesBuffer;
+    GLuint mCurrentPixelBuffer;
+
+    const void* mCurrentPositionPointer;
+    GLsizei mCurrentPositionStride;
+    const void* mCurrentTexCoordsPointer;
+    GLsizei mCurrentTexCoordsStride;
+
+    bool mTexCoordsArrayEnabled;
+
+    // Global index buffer
+    GLuint mQuadListIndices;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_MESHSTATE_H
diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp
new file mode 100644
index 0000000..c23af52
--- /dev/null
+++ b/libs/hwui/renderstate/PixelBufferState.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+#include "renderstate/PixelBufferState.h"
+
+namespace android {
+namespace uirenderer {
+
+PixelBufferState::PixelBufferState()
+        : mCurrentPixelBuffer(0) {
+}
+
+bool PixelBufferState::bind(GLuint buffer) {
+    if (mCurrentPixelBuffer != buffer) {
+        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
+        mCurrentPixelBuffer = buffer;
+        return true;
+    }
+    return false;
+}
+
+bool PixelBufferState::unbind() {
+    if (mCurrentPixelBuffer) {
+        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+        mCurrentPixelBuffer = 0;
+        return true;
+    }
+    return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h
new file mode 100644
index 0000000..8dab21d
--- /dev/null
+++ b/libs/hwui/renderstate/PixelBufferState.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_PIXELBUFFERSTATE_H
+#define RENDERSTATE_PIXELBUFFERSTATE_H
+
+#include <GLES3/gl3.h>
+
+namespace android {
+namespace uirenderer {
+
+class PixelBufferState {
+    friend class Caches; // TODO: move to RenderState
+public:
+    bool bind(GLuint buffer);
+    bool unbind();
+private:
+    PixelBufferState();
+    GLuint mCurrentPixelBuffer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_PIXELBUFFERSTATE_H
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
new file mode 100644
index 0000000..ba49833
--- /dev/null
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "renderstate/RenderState.h"
+
+#include "renderthread/CanvasContext.h"
+#include "renderthread/EglManager.h"
+#include "utils/GLUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+RenderState::RenderState(renderthread::RenderThread& thread)
+        : mRenderThread(thread)
+        , mViewportWidth(0)
+        , mViewportHeight(0)
+        , mFramebuffer(0) {
+    mThreadId = pthread_self();
+}
+
+RenderState::~RenderState() {
+    LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
+            "State object lifecycle not managed correctly");
+}
+
+void RenderState::onGLContextCreated() {
+    LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
+            "State object lifecycle not managed correctly");
+    mBlend = new Blend();
+    mMeshState = new MeshState();
+    mScissor = new Scissor();
+    mStencil = new Stencil();
+
+    // This is delayed because the first access of Caches makes GL calls
+    if (!mCaches) {
+        mCaches = &Caches::createInstance(*this);
+    }
+    mCaches->init();
+    mCaches->textureCache.setAssetAtlas(&mAssetAtlas);
+}
+
+static void layerLostGlContext(Layer* layer) {
+    layer->onGlContextLost();
+}
+
+void RenderState::onGLContextDestroyed() {
+/*
+    size_t size = mActiveLayers.size();
+    if (CC_UNLIKELY(size != 0)) {
+        ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d",
+                mRegisteredContexts.size(), size, mActiveLayers.empty());
+        mCaches->dumpMemoryUsage();
+        for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin();
+                cit != mRegisteredContexts.end(); cit++) {
+            renderthread::CanvasContext* context = *cit;
+            ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get());
+            ALOGE("  Prefeteched layers: %zu", context->mPrefetechedLayers.size());
+            for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin();
+                    pit != context->mPrefetechedLayers.end(); pit++) {
+                (*pit)->debugDumpLayers("    ");
+            }
+            context->mRootRenderNode->debugDumpLayers("  ");
+        }
+
+
+        if (mActiveLayers.begin() == mActiveLayers.end()) {
+            ALOGE("set has become empty. wat.");
+        }
+        for (std::set<const Layer*>::iterator lit = mActiveLayers.begin();
+             lit != mActiveLayers.end(); lit++) {
+            const Layer* layer = *(lit);
+            ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d",
+                    layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered);
+        }
+        LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size);
+    }
+*/
+
+    // TODO: reset all cached state in state objects
+    std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
+    mAssetAtlas.terminate();
+
+    mCaches->terminate();
+
+    delete mBlend;
+    mBlend = nullptr;
+    delete mMeshState;
+    mMeshState = nullptr;
+    delete mScissor;
+    mScissor = nullptr;
+    delete mStencil;
+    mStencil = nullptr;
+}
+
+void RenderState::setViewport(GLsizei width, GLsizei height) {
+    mViewportWidth = width;
+    mViewportHeight = height;
+    glViewport(0, 0, mViewportWidth, mViewportHeight);
+}
+
+
+void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
+    *outWidth = mViewportWidth;
+    *outHeight = mViewportHeight;
+}
+
+void RenderState::bindFramebuffer(GLuint fbo) {
+    if (mFramebuffer != fbo) {
+        mFramebuffer = fbo;
+        glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    }
+}
+
+void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
+    interruptForFunctorInvoke();
+    (*functor)(mode, info);
+    resumeFromFunctorInvoke();
+}
+
+void RenderState::interruptForFunctorInvoke() {
+    mCaches->setProgram(nullptr);
+    mCaches->textureState().resetActiveTexture();
+    meshState().unbindMeshBuffer();
+    meshState().unbindIndicesBuffer();
+    meshState().resetVertexPointers();
+    meshState().disableTexCoordsVertexArray();
+    debugOverdraw(false, false);
+}
+
+void RenderState::resumeFromFunctorInvoke() {
+    glViewport(0, 0, mViewportWidth, mViewportHeight);
+    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    debugOverdraw(false, false);
+
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+    scissor().invalidate();
+    blend().invalidate();
+
+    mCaches->textureState().activateTexture(0);
+    mCaches->textureState().resetBoundTextures();
+}
+
+void RenderState::debugOverdraw(bool enable, bool clear) {
+    if (mCaches->debugOverdraw && mFramebuffer == 0) {
+        if (clear) {
+            scissor().setEnabled(false);
+            stencil().clear();
+        }
+        if (enable) {
+            stencil().enableDebugWrite();
+        } else {
+            stencil().disable();
+        }
+    }
+}
+
+void RenderState::requireGLContext() {
+    assertOnGLThread();
+    mRenderThread.eglManager().requireGlContext();
+}
+
+void RenderState::assertOnGLThread() {
+    pthread_t curr = pthread_self();
+    LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!");
+}
+
+class DecStrongTask : public renderthread::RenderTask {
+public:
+    DecStrongTask(VirtualLightRefBase* object) : mObject(object) {}
+
+    virtual void run() override {
+        mObject->decStrong(nullptr);
+        mObject = nullptr;
+        delete this;
+    }
+
+private:
+    VirtualLightRefBase* mObject;
+};
+
+void RenderState::postDecStrong(VirtualLightRefBase* object) {
+    mRenderThread.queue(new DecStrongTask(object));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Render
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Not yet supported:
+ *
+ * Textures + coordinates
+ * SkiaShader
+ * ColorFilter
+ *
+    // TODO: texture coord
+    // TODO: texture support
+    // TODO: skiashader support
+    // TODO: color filter support
+ */
+
+void RenderState::render(const Glop& glop) {
+    const Glop::Mesh& mesh = glop.mesh;
+    const Glop::Fill& shader = glop.fill;
+
+    // --------------------------------------------
+    // ---------- Shader + uniform setup ----------
+    // --------------------------------------------
+    mCaches->setProgram(shader.program);
+
+    Glop::FloatColor color = shader.color;
+    shader.program->setColor(color.r, color.g, color.b, color.a);
+
+    shader.program->set(glop.transform.ortho,
+            glop.transform.modelView,
+            glop.transform.canvas,
+            glop.transform.fudgingOffset);
+
+    if (glop.fill.filterMode == ProgramDescription::kColorBlend) {
+        const Glop::FloatColor& color = glop.fill.filter.color;
+        glUniform4f(mCaches->program().getUniform("colorBlend"),
+                color.r, color.g, color.b, color.a);
+    } else if (glop.fill.filterMode == ProgramDescription::kColorMatrix) {
+        glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE,
+                glop.fill.filter.matrix.matrix);
+        glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1,
+                glop.fill.filter.matrix.vector);
+    }
+
+    // --------------------------------
+    // ---------- Mesh setup ----------
+    // --------------------------------
+    // vertices
+    bool force = meshState().bindMeshBufferInternal(mesh.vertexBufferObject)
+            || (mesh.vertices != nullptr);
+    meshState().bindPositionVertexPointer(force, mesh.vertices, mesh.stride);
+
+    // indices
+    meshState().bindIndicesBufferInternal(mesh.indexBufferObject);
+
+    if (glop.mesh.vertexFlags & kTextureCoord_Attrib) {
+        // TODO: support textures
+        LOG_ALWAYS_FATAL("textures not yet supported");
+    } else {
+        meshState().disableTexCoordsVertexArray();
+    }
+    if (glop.mesh.vertexFlags & kColor_Attrib) {
+        LOG_ALWAYS_FATAL("color vertex attribute not yet supported");
+        // TODO: enable color, disable when done
+    }
+    int alphaSlot = -1;
+    if (glop.mesh.vertexFlags & kAlpha_Attrib) {
+        const void* alphaCoords = ((const GLbyte*) glop.mesh.vertices) + kVertexAlphaOffset;
+        alphaSlot = shader.program->getAttrib("vtxAlpha");
+        glEnableVertexAttribArray(alphaSlot);
+        glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords);
+    }
+
+    // ------------------------------------
+    // ---------- GL state setup ----------
+    // ------------------------------------
+    blend().setFactors(glop.blend.src, glop.blend.dst);
+
+    // ------------------------------------
+    // ---------- GL state setup ----------
+    // ------------------------------------
+    if (mesh.indexBufferObject || mesh.indices) {
+        glDrawElements(glop.mesh.primitiveMode, glop.mesh.vertexCount,
+                GL_UNSIGNED_SHORT, mesh.indices);
+    } else {
+        glDrawArrays(glop.mesh.primitiveMode, 0, glop.mesh.vertexCount);
+    }
+
+    if (glop.mesh.vertexFlags & kAlpha_Attrib) {
+        glDisableVertexAttribArray(alphaSlot);
+    }
+}
+
+void RenderState::dump() {
+    blend().dump();
+    meshState().dump();
+    scissor().dump();
+    stencil().dump();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderState.h b/libs/hwui/renderstate/RenderState.h
similarity index 79%
rename from libs/hwui/RenderState.h
rename to libs/hwui/renderstate/RenderState.h
index 629fe0d..4fd792c 100644
--- a/libs/hwui/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -22,11 +22,16 @@
 #include <utils/Mutex.h>
 #include <utils/Functor.h>
 #include <utils/RefBase.h>
-
 #include <private/hwui/DrawGlInfo.h>
+#include <renderstate/Blend.h>
 
 #include "AssetAtlas.h"
 #include "Caches.h"
+#include "Glop.h"
+#include "renderstate/MeshState.h"
+#include "renderstate/PixelBufferState.h"
+#include "renderstate/Scissor.h"
+#include "renderstate/Stencil.h"
 #include "utils/Macros.h"
 
 namespace android {
@@ -58,10 +63,10 @@
 
     void debugOverdraw(bool enable, bool clear);
 
-    void registerLayer(const Layer* layer) {
+    void registerLayer(Layer* layer) {
         mActiveLayers.insert(layer);
     }
-    void unregisterLayer(const Layer* layer) {
+    void unregisterLayer(Layer* layer) {
         mActiveLayers.erase(layer);
     }
 
@@ -79,8 +84,15 @@
     // more thinking...
     void postDecStrong(VirtualLightRefBase* object);
 
-    AssetAtlas& assetAtlas() { return mAssetAtlas; }
+    void render(const Glop& glop);
 
+    AssetAtlas& assetAtlas() { return mAssetAtlas; }
+    Blend& blend() { return *mBlend; }
+    MeshState& meshState() { return *mMeshState; }
+    Scissor& scissor() { return *mScissor; }
+    Stencil& stencil() { return *mStencil; }
+
+    void dump();
 private:
     friend class renderthread::RenderThread;
     friend class Caches;
@@ -92,10 +104,17 @@
     RenderState(renderthread::RenderThread& thread);
     ~RenderState();
 
+
     renderthread::RenderThread& mRenderThread;
-    Caches* mCaches;
+    Caches* mCaches = nullptr;
+
+    Blend* mBlend = nullptr;
+    MeshState* mMeshState = nullptr;
+    Scissor* mScissor = nullptr;
+    Stencil* mStencil = nullptr;
+
     AssetAtlas mAssetAtlas;
-    std::set<const Layer*> mActiveLayers;
+    std::set<Layer*> mActiveLayers;
     std::set<renderthread::CanvasContext*> mRegisteredContexts;
 
     GLsizei mViewportWidth;
diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp
new file mode 100644
index 0000000..95dcd18
--- /dev/null
+++ b/libs/hwui/renderstate/Scissor.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+#include "renderstate/Scissor.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+Scissor::Scissor()
+    : mEnabled(false)
+    , mScissorX(0)
+    , mScissorY(0)
+    , mScissorWidth(0)
+    , mScissorHeight(0) {
+}
+
+bool Scissor::setEnabled(bool enabled) {
+    if (mEnabled != enabled) {
+        if (enabled) {
+            glEnable(GL_SCISSOR_TEST);
+        } else {
+            glDisable(GL_SCISSOR_TEST);
+        }
+        mEnabled = enabled;
+        return true;
+    }
+    return false;
+}
+
+bool Scissor::set(GLint x, GLint y, GLint width, GLint height) {
+    if (mEnabled && (x != mScissorX || y != mScissorY
+            || width != mScissorWidth || height != mScissorHeight)) {
+
+        if (x < 0) {
+            width += x;
+            x = 0;
+        }
+        if (y < 0) {
+            height += y;
+            y = 0;
+        }
+        if (width < 0) {
+            width = 0;
+        }
+        if (height < 0) {
+            height = 0;
+        }
+        glScissor(x, y, width, height);
+
+        mScissorX = x;
+        mScissorY = y;
+        mScissorWidth = width;
+        mScissorHeight = height;
+
+        return true;
+    }
+    return false;
+}
+
+void Scissor::reset() {
+    mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
+}
+
+void Scissor::invalidate() {
+    mEnabled = glIsEnabled(GL_SCISSOR_TEST);
+    setEnabled(true);
+    reset();
+}
+
+void Scissor::dump() {
+    ALOGD("Scissor: enabled %d, %d %d %d %d",
+            mEnabled, mScissorX, mScissorY, mScissorWidth, mScissorHeight);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h
new file mode 100644
index 0000000..b37ec58
--- /dev/null
+++ b/libs/hwui/renderstate/Scissor.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_SCISSOR_H
+#define RENDERSTATE_SCISSOR_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+class Scissor {
+    friend class RenderState;
+public:
+    bool setEnabled(bool enabled);
+    bool set(GLint x, GLint y, GLint width, GLint height);
+    void reset();
+    bool isEnabled() { return mEnabled; }
+    void dump();
+private:
+    Scissor();
+    void invalidate();
+    bool mEnabled;
+    GLint mScissorX;
+    GLint mScissorY;
+    GLint mScissorWidth;
+    GLint mScissorHeight;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_SCISSOR_H
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp
similarity index 94%
rename from libs/hwui/Stencil.cpp
rename to libs/hwui/renderstate/Stencil.cpp
index f56a02e..cedb233 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/renderstate/Stencil.cpp
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
+#include "renderstate/Stencil.h"
+
+#include "Caches.h"
 #include "Debug.h"
 #include "Extensions.h"
 #include "Properties.h"
-#include "Stencil.h"
 
 #include <GLES2/gl2ext.h>
 
@@ -42,7 +44,7 @@
 
 GLenum Stencil::getSmallestStencilFormat() {
 #if !DEBUG_STENCIL
-    const Extensions& extensions = Extensions::getInstance();
+    const Extensions& extensions = Caches::getInstance().extensions();
     if (extensions.has1BitStencil()) {
         return GL_STENCIL_INDEX1_OES;
     } else if (extensions.has4BitStencil()) {
@@ -123,5 +125,9 @@
     }
 }
 
+void Stencil::dump() {
+    ALOGD("Stencil: state %d", mState);
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Stencil.h b/libs/hwui/renderstate/Stencil.h
similarity index 99%
rename from libs/hwui/Stencil.h
rename to libs/hwui/renderstate/Stencil.h
index 20bb955..a88beae 100644
--- a/libs/hwui/Stencil.h
+++ b/libs/hwui/renderstate/Stencil.h
@@ -98,6 +98,8 @@
         return mState == kTest;
     }
 
+    void dump();
+
 private:
     void enable();
 
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
new file mode 100644
index 0000000..1a638d2
--- /dev/null
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+#include <renderstate/TextureState.h>
+
+namespace android {
+namespace uirenderer {
+
+// Must define as many texture units as specified by kTextureUnitsCount
+const GLenum kTextureUnits[] = {
+    GL_TEXTURE0,
+    GL_TEXTURE1,
+    GL_TEXTURE2
+};
+
+TextureState::TextureState()
+        : mTextureUnit(0) {
+    glActiveTexture(kTextureUnits[0]);
+    resetBoundTextures();
+
+    GLint maxTextureUnits;
+    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+    LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount,
+        "At least %d texture units are required!", kTextureUnitsCount);
+}
+
+void TextureState::activateTexture(GLuint textureUnit) {
+    if (mTextureUnit != textureUnit) {
+        glActiveTexture(kTextureUnits[textureUnit]);
+        mTextureUnit = textureUnit;
+    }
+}
+
+void TextureState::resetActiveTexture() {
+    mTextureUnit = -1;
+}
+
+void TextureState::bindTexture(GLuint texture) {
+    if (mBoundTextures[mTextureUnit] != texture) {
+        glBindTexture(GL_TEXTURE_2D, texture);
+        mBoundTextures[mTextureUnit] = texture;
+    }
+}
+
+void TextureState::bindTexture(GLenum target, GLuint texture) {
+    if (target == GL_TEXTURE_2D) {
+        bindTexture(texture);
+    } else {
+        // GLConsumer directly calls glBindTexture() with
+        // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
+        // since the cached state could be stale
+        glBindTexture(target, texture);
+    }
+}
+
+void TextureState::deleteTexture(GLuint texture) {
+    // When glDeleteTextures() is called on a currently bound texture,
+    // OpenGL ES specifies that the texture is then considered unbound
+    // Consider the following series of calls:
+    //
+    // glGenTextures -> creates texture name 2
+    // glBindTexture(2)
+    // glDeleteTextures(2) -> 2 is now unbound
+    // glGenTextures -> can return 2 again
+    //
+    // If we don't call glBindTexture(2) after the second glGenTextures
+    // call, any texture operation will be performed on the default
+    // texture (name=0)
+
+    unbindTexture(texture);
+
+    glDeleteTextures(1, &texture);
+}
+
+void TextureState::resetBoundTextures() {
+    for (int i = 0; i < kTextureUnitsCount; i++) {
+        mBoundTextures[i] = 0;
+    }
+}
+
+void TextureState::unbindTexture(GLuint texture) {
+    for (int i = 0; i < kTextureUnitsCount; i++) {
+        if (mBoundTextures[i] == texture) {
+            mBoundTextures[i] = 0;
+        }
+    }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
new file mode 100644
index 0000000..5a57b9f
--- /dev/null
+++ b/libs/hwui/renderstate/TextureState.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+#ifndef RENDERSTATE_TEXTURESTATE_H
+#define RENDERSTATE_TEXTURESTATE_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class TextureState {
+    friend class Caches; // TODO: move to RenderState
+public:
+    /**
+     * Activate the specified texture unit. The texture unit must
+     * be specified using an integer number (0 for GL_TEXTURE0 etc.)
+     */
+    void activateTexture(GLuint textureUnit);
+
+    /**
+     * Invalidate the cached value of the active texture unit.
+     */
+    void resetActiveTexture();
+
+    /**
+     * Binds the specified texture as a GL_TEXTURE_2D texture.
+     * All texture bindings must be performed with this method or
+     * bindTexture(GLenum, GLuint).
+     */
+    void bindTexture(GLuint texture);
+
+    /**
+     * Binds the specified texture with the specified render target.
+     * All texture bindings must be performed with this method or
+     * bindTexture(GLuint).
+     */
+    void bindTexture(GLenum target, GLuint texture);
+
+    /**
+     * Deletes the specified texture and clears it from the cache
+     * of bound textures.
+     * All textures must be deleted using this method.
+     */
+    void deleteTexture(GLuint texture);
+
+    /**
+     * Signals that the cache of bound textures should be cleared.
+     * Other users of the context may have altered which textures are bound.
+     */
+    void resetBoundTextures();
+
+    /**
+     * Clear the cache of bound textures.
+     */
+    void unbindTexture(GLuint texture);
+private:
+    // total number of texture units available for use
+    static const int kTextureUnitsCount = 3;
+
+    TextureState();
+    GLuint mTextureUnit;
+
+    // Caches texture bindings for the GL_TEXTURE_2D target
+    GLuint mBoundTextures[kTextureUnitsCount];
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 71ecba5..6346479 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,19 +16,19 @@
 
 #include "CanvasContext.h"
 
-#include <algorithm>
-#include <private/hwui/DrawGlInfo.h>
-#include <strings.h>
-
 #include "EglManager.h"
 #include "RenderThread.h"
 #include "../AnimationContext.h"
 #include "../Caches.h"
 #include "../DeferredLayerUpdater.h"
-#include "../RenderState.h"
+#include "../renderstate/RenderState.h"
+#include "../renderstate/Stencil.h"
 #include "../LayerRenderer.h"
 #include "../OpenGLRenderer.h"
-#include "../Stencil.h"
+
+#include <algorithm>
+#include <private/hwui/DrawGlInfo.h>
+#include <strings.h>
 
 #define TRIM_MEMORY_COMPLETE 80
 #define TRIM_MEMORY_UI_HIDDEN 20
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 0aa0439..3afca2f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,16 +16,19 @@
 
 #include "EglManager.h"
 
+#include "../Caches.h"
+#include "../renderstate/RenderState.h"
+#include "RenderThread.h"
+
 #include <cutils/log.h>
 #include <cutils/properties.h>
-
-#include "../Caches.h"
-#include "../RenderState.h"
-#include "RenderThread.h"
+#include <EGL/eglext.h>
 
 #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
 #define GLES_VERSION 2
 
+#define WAIT_FOR_GPU_COMPLETION 0
+
 // Android-specific addition that is used to show when frames began in systrace
 EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
 
@@ -214,9 +217,6 @@
     if (mEglDisplay == EGL_NO_DISPLAY) return;
 
     usePBufferSurface();
-    if (Caches::hasInstance()) {
-        Caches::getInstance().terminate();
-    }
 
     mRenderThread.renderState().onGLContextDestroyed();
     eglDestroyContext(mEglDisplay, mEglContext);
@@ -263,6 +263,14 @@
 
 bool EglManager::swapBuffers(EGLSurface surface) {
     mInFrame = false;
+
+#if WAIT_FOR_GPU_COMPLETION
+    {
+        ATRACE_NAME("Finishing GPU work");
+        fence();
+    }
+#endif
+
     eglSwapBuffers(mEglDisplay, surface);
     EGLint err = eglGetError();
     if (CC_LIKELY(err == EGL_SUCCESS)) {
@@ -281,6 +289,13 @@
     return false;
 }
 
+void EglManager::fence() {
+    EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
+    eglClientWaitSyncKHR(mEglDisplay, fence,
+            EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
+    eglDestroySyncKHR(mEglDisplay, fence);
+}
+
 void EglManager::cancelFrame() {
     mInFrame = false;
 }
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index e12db3a..b1a18a9 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -55,6 +55,8 @@
 
     void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
 
+    void fence();
+
 private:
     friend class RenderThread;
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a2f0625..9a0fbad 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,17 +16,15 @@
 
 #include "RenderThread.h"
 
-#if defined(HAVE_PTHREADS)
-#include <sys/resource.h>
-#endif
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-
-#include "../RenderState.h"
+#include "../renderstate/RenderState.h"
 #include "CanvasContext.h"
 #include "EglManager.h"
 #include "RenderProxy.h"
 
+#include <gui/DisplayEventReceiver.h>
+#include <sys/resource.h>
+#include <utils/Log.h>
+
 namespace android {
 using namespace uirenderer::renderthread;
 ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
@@ -250,9 +248,7 @@
 }
 
 bool RenderThread::threadLoop() {
-#if defined(HAVE_PTHREADS)
     setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
-#endif
     initThreadLocals();
 
     int timeoutMillis = -1;
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index a12dac7..5f445f4 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -42,7 +42,8 @@
 
 static DisplayListRenderer* startRecording(RenderNode* node) {
     DisplayListRenderer* renderer = new DisplayListRenderer();
-    renderer->setViewport(node->getWidth(), node->getHeight());
+    renderer->setViewport(node->stagingProperties().getWidth(),
+            node->stagingProperties().getHeight());
     renderer->prepare();
     return renderer;
 }
@@ -53,80 +54,214 @@
     delete renderer;
 }
 
-sp<RenderNode> createCard(int x, int y, int width, int height) {
-    sp<RenderNode> node = new RenderNode();
-    node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-    node->mutateStagingProperties().setElevation(dp(16));
-    node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
-    node->mutateStagingProperties().mutableOutline().setShouldClip(true);
-    node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+class TreeContentAnimation {
+public:
+    virtual ~TreeContentAnimation() {}
+    virtual int getFrameCount() { return 150; }
+    virtual void createContent(int width, int height, DisplayListRenderer* renderer) = 0;
+    virtual void doFrame(int frameNr) = 0;
 
-    DisplayListRenderer* renderer = startRecording(node.get());
-    renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
-    endRecording(renderer, node.get());
+    template <class T>
+    static void run() {
+        T animation;
 
-    return node;
-}
+        TestContext testContext;
 
-int main(int argc, char* argv[]) {
-    TestContext testContext;
+        // create the native surface
+        const int width = gDisplay.w;
+        const int height = gDisplay.h;
+        sp<Surface> surface = testContext.surface();
 
-    // create the native surface
-    const int width = gDisplay.w;
-    const int height = gDisplay.h;
-    sp<Surface> surface = testContext.surface();
+        RenderNode* rootNode = new RenderNode();
+        rootNode->incStrong(nullptr);
+        rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+        rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        rootNode->mutateStagingProperties().setClipToBounds(false);
+        rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
 
-    RenderNode* rootNode = new RenderNode();
-    rootNode->incStrong(nullptr);
-    rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
-    rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-    rootNode->mutateStagingProperties().setClipToBounds(false);
-    rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+        ContextFactory factory;
+        std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+        proxy->loadSystemProperties();
+        proxy->initialize(surface);
+        float lightX = width / 2.0;
+        proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
+                dp(800.0f), 255 * 0.075, 255 * 0.15);
 
-    ContextFactory factory;
-    std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
-    proxy->loadSystemProperties();
-    proxy->initialize(surface);
-    float lightX = width / 2.0;
-    proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
-            dp(800.0f), 255 * 0.075, 255 * 0.15);
+        android::uirenderer::Rect DUMMY;
 
-    android::uirenderer::Rect DUMMY;
+        std::vector< sp<RenderNode> > cards;
 
-    std::vector< sp<RenderNode> > cards;
+        DisplayListRenderer* renderer = startRecording(rootNode);
+        animation.createContent(width, height, renderer);
+        endRecording(renderer, rootNode);
 
-    DisplayListRenderer* renderer = startRecording(rootNode);
-    renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-    renderer->insertReorderBarrier(true);
+        for (int i = 0; i < 150; i++) {
+            testContext.waitForVsync();
 
-    for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
-        for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
-            sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
-            renderer->drawRenderNode(card.get(), DUMMY, 0);
-            cards.push_back(card);
+            ATRACE_NAME("UI-Draw Frame");
+            animation.doFrame(i);
+            nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
+            proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
         }
+
+        sleep(5);
+
+        rootNode->decStrong(nullptr);
     }
+};
 
-    renderer->insertReorderBarrier(false);
-    endRecording(renderer, rootNode);
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+    std::vector< sp<RenderNode> > cards;
+    void createContent(int width, int height, DisplayListRenderer* renderer) override {
+        android::uirenderer::Rect DUMMY;
 
-    for (int i = 0; i < 150; i++) {
-        testContext.waitForVsync();
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
 
-        ATRACE_NAME("UI-Draw Frame");
+        for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+            for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+                sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+                renderer->drawRenderNode(card.get(), DUMMY, 0);
+                cards.push_back(card);
+            }
+        }
+
+        renderer->insertReorderBarrier(false);
+    }
+    void doFrame(int frameNr) override {
         for (size_t ci = 0; ci < cards.size(); ci++) {
-            cards[ci]->mutateStagingProperties().setTranslationX(i);
-            cards[ci]->mutateStagingProperties().setTranslationY(i);
+            cards[ci]->mutateStagingProperties().setTranslationX(frameNr);
+            cards[ci]->mutateStagingProperties().setTranslationY(frameNr);
             cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
         }
-        nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
-        proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->mutateStagingProperties().setElevation(dp(16));
+        node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+        node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+        DisplayListRenderer* renderer = startRecording(node.get());
+        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, DisplayListRenderer* renderer) override {
+        android::uirenderer::Rect DUMMY;
+
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
+
+        card = createCard(40, 40, 200, 200);
+        renderer->drawRenderNode(card.get(), DUMMY, 0);
+
+        renderer->insertReorderBarrier(false);
+    }
+    void doFrame(int frameNr) override {
+        card->mutateStagingProperties().setTranslationX(frameNr);
+        card->mutateStagingProperties().setTranslationY(frameNr);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListRenderer* renderer = startRecording(node.get());
+        renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+        float rects[width * height];
+        int index = 0;
+        for (int xOffset = 0; xOffset < width; xOffset+=2) {
+            for (int yOffset = 0; yOffset < height; yOffset+=2) {
+                rects[index++] = xOffset;
+                rects[index++] = yOffset;
+                rects[index++] = xOffset + 1;
+                rects[index++] = yOffset + 1;
+            }
+        }
+        int count = width * height;
+
+        SkPaint paint;
+        paint.setColor(0xff00ffff);
+        renderer->drawRects(rects, count, &paint);
+
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, DisplayListRenderer* renderer) override {
+        android::uirenderer::Rect DUMMY;
+
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
+
+        card = createCard(40, 40, 200, 200);
+        renderer->drawRenderNode(card.get(), DUMMY, 0);
+
+        renderer->insertReorderBarrier(false);
     }
 
-    sleep(5);
+    void doFrame(int frameNr) override {
+        card->mutateStagingProperties().setTranslationX(frameNr);
+        card->mutateStagingProperties().setTranslationY(frameNr);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
 
-    rootNode->decStrong(nullptr);
+        DisplayListRenderer* renderer = startRecording(node.get());
+        renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
 
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF00FFFF);
+        renderer->drawOval(0, 0, width, height, paint);
+
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+
+struct cstr_cmp {
+    bool operator()(const char *a, const char *b) const {
+        return std::strcmp(a, b) < 0;
+    }
+};
+
+typedef void (*testProc)();
+
+std::map<const char*, testProc, cstr_cmp> gTestMap {
+    {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
+    {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
+    {"oval", TreeContentAnimation::run<OvalAnimation> },
+};
+
+int main(int argc, char* argv[]) {
+    const char* testName = argc > 1 ? argv[1] : "shadowgrid";
+    testProc proc = gTestMap[testName];
+    if(!proc) {
+        printf("Error: couldn't find test %s\n", testName);
+        return 1;
+    }
+    proc();
     printf("Success!\n");
     return 0;
 }
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index cb5401c..c69b2fd 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-#include <sys/sysinfo.h>
-#if defined(HAVE_PTHREADS)
 #include <sys/resource.h>
-#endif
+#include <sys/sysinfo.h>
 
 #include "TaskManager.h"
 #include "Task.h"
@@ -83,9 +81,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 status_t TaskManager::WorkerThread::readyToRun() {
-#if defined(HAVE_PTHREADS)
     setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
-#endif
     return NO_ERROR;
 }
 
@@ -109,6 +105,8 @@
 bool TaskManager::WorkerThread::addTask(TaskWrapper task) {
     if (!isRunning()) {
         run(mName.string(), PRIORITY_DEFAULT);
+    } else if (exitPending()) {
+        return false;
     }
 
     Mutex::Autolock l(mLock);
@@ -124,10 +122,6 @@
 }
 
 void TaskManager::WorkerThread::exit() {
-    {
-        Mutex::Autolock l(mLock);
-        mTasks.clear();
-    }
     requestExit();
     mSignal.signal();
 }
diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h
index eb4ab64..ec6519c 100644
--- a/libs/hwui/thread/TaskProcessor.h
+++ b/libs/hwui/thread/TaskProcessor.h
@@ -30,9 +30,6 @@
     TaskProcessorBase() { }
     virtual ~TaskProcessorBase() { };
 
-private:
-    friend class TaskManager;
-
     virtual void process(const sp<TaskBase>& task) = 0;
 };
 
@@ -44,9 +41,6 @@
 
     bool add(const sp<Task<T> >& task);
 
-    virtual void onProcess(const sp<Task<T> >& task) = 0;
-
-private:
     virtual void process(const sp<TaskBase>& task) override {
         sp<Task<T> > realTask = static_cast<Task<T>* >(task.get());
         // This is the right way to do it but sp<> doesn't play nice
@@ -54,6 +48,8 @@
         onProcess(realTask);
     }
 
+    virtual void onProcess(const sp<Task<T> >& task) = 0;
+
     TaskManager* mManager;
 };
 
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 5b7c87c..fe43fdb 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -18,8 +18,8 @@
 
 #define PREVENT_COPY_AND_ASSIGN(Type) \
     private: \
-        Type(const Type&); \
-        void operator=(const Type&)
+        Type(const Type&) = delete; \
+        void operator=(const Type&) = delete
 
 #define DESCRIPTION_TYPE(Type) \
         int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index fa0ae03..679e2bf 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,6 +16,9 @@
 #ifndef PAINT_UTILS_H
 #define PAINT_UTILS_H
 
+#include <SkColorFilter.h>
+#include <SkXfermode.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -49,6 +52,13 @@
                 && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
     }
 
+    static bool isBlendedShader(const SkShader* shader) {
+        if (shader == nullptr) {
+            return false;
+        }
+        return !shader->isOpaque();
+    }
+
     static bool isBlendedColorFilter(const SkColorFilter* filter) {
         if (filter == nullptr) {
             return false;
diff --git a/location/java/android/location/GpsSatellite.java b/location/java/android/location/GpsSatellite.java
index 17af4a6..820f5746 100644
--- a/location/java/android/location/GpsSatellite.java
+++ b/location/java/android/location/GpsSatellite.java
@@ -40,13 +40,17 @@
      * cached GpsStatus instance to the client's copy.
      */
     void setStatus(GpsSatellite satellite) {
-        mValid = satellite.mValid;
-        mHasEphemeris = satellite.mHasEphemeris;
-        mHasAlmanac = satellite.mHasAlmanac;
-        mUsedInFix = satellite.mUsedInFix;
-        mSnr = satellite.mSnr;
-        mElevation = satellite.mElevation;
-        mAzimuth = satellite.mAzimuth;
+        if (satellite == null) {
+            mValid = false;
+        } else {
+            mValid = satellite.mValid;
+            mHasEphemeris = satellite.mHasEphemeris;
+            mHasAlmanac = satellite.mHasAlmanac;
+            mUsedInFix = satellite.mUsedInFix;
+            mSnr = satellite.mSnr;
+            mElevation = satellite.mElevation;
+            mAzimuth = satellite.mAzimuth;
+        }
     }
 
     /**
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 4af55a6..323f326 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -16,6 +16,8 @@
 
 package android.location;
 
+import android.util.SparseArray;
+
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
@@ -29,20 +31,24 @@
 
     /* These package private values are modified by the LocationManager class */
     private int mTimeToFirstFix;
-    private GpsSatellite mSatellites[] = new GpsSatellite[NUM_SATELLITES];
+    private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>();
 
     private final class SatelliteIterator implements Iterator<GpsSatellite> {
 
-        private GpsSatellite[] mSatellites;
-        int mIndex = 0;
+        private final SparseArray<GpsSatellite> mSatellites;
+        private final int mSatellitesCount;
 
-        SatelliteIterator(GpsSatellite[] satellites) {
+        private int mIndex = 0;
+
+        SatelliteIterator(SparseArray<GpsSatellite> satellites) {
             mSatellites = satellites;
+            mSatellitesCount = satellites.size();
         }
 
         public boolean hasNext() {
-            for (int i = mIndex; i < mSatellites.length; i++) {
-                if (mSatellites[i].mValid) {
+            for (; mIndex < mSatellitesCount; ++mIndex) {
+                GpsSatellite satellite = mSatellites.valueAt(mIndex);
+                if (satellite.mValid) {
                     return true;
                 }
             }
@@ -50,8 +56,9 @@
         }
 
         public GpsSatellite next() {
-            while (mIndex < mSatellites.length) {
-                GpsSatellite satellite = mSatellites[mIndex++];
+            while (mIndex < mSatellitesCount) {
+                GpsSatellite satellite = mSatellites.valueAt(mIndex);
+                ++mIndex;
                 if (satellite.mValid) {
                     return satellite;
                 }
@@ -106,7 +113,7 @@
          * <li> {@link GpsStatus#GPS_EVENT_SATELLITE_STATUS}
          * </ul>
          *
-         * When this method is called, the client should call 
+         * When this method is called, the client should call
          * {@link LocationManager#getGpsStatus} to get additional
          * status information.
          *
@@ -127,11 +134,8 @@
         void onNmeaReceived(long timestamp, String nmea);
     }
 
-    GpsStatus() {
-        for (int i = 0; i < mSatellites.length; i++) {
-            mSatellites[i] = new GpsSatellite(i + 1);
-        }
-    }
+    // For API-compat a public ctor() is not available
+    GpsStatus() {}
 
     /**
      * Used internally within {@link LocationManager} to copy GPS status
@@ -141,18 +145,17 @@
     synchronized void setStatus(int svCount, int[] prns, float[] snrs,
             float[] elevations, float[] azimuths, int ephemerisMask,
             int almanacMask, int usedInFixMask) {
-        int i;
+        clearSatellites();
+        for (int i = 0; i < svCount; i++) {
+            int prn = prns[i];
+            int prnShift = (1 << (prn - 1));
+            if (prn > 0 && prn <= NUM_SATELLITES) {
+                GpsSatellite satellite = mSatellites.get(prn);
+                if (satellite == null) {
+                    satellite = new GpsSatellite(prn);
+                    mSatellites.put(prn, satellite);
+                }
 
-        for (i = 0; i < mSatellites.length; i++) {
-            mSatellites[i].mValid = false;
-        }
-        
-        for (i = 0; i < svCount; i++) {
-            int prn = prns[i] - 1;
-            int prnShift = (1 << prn);
-            if (prn >= 0 && prn < mSatellites.length) {
-                GpsSatellite satellite = mSatellites[prn];
-    
                 satellite.mValid = true;
                 satellite.mSnr = snrs[i];
                 satellite.mElevation = elevations[i];
@@ -172,10 +175,38 @@
      */
     void setStatus(GpsStatus status) {
         mTimeToFirstFix = status.getTimeToFirstFix();
+        clearSatellites();
 
-        for (int i = 0; i < mSatellites.length; i++) {
-            mSatellites[i].setStatus(status.mSatellites[i]);
-        } 
+        SparseArray<GpsSatellite> otherSatellites = status.mSatellites;
+        int otherSatellitesCount = otherSatellites.size();
+        int satelliteIndex = 0;
+        // merge both sparse arrays, note that we have already invalidated the elements in the
+        // receiver array
+        for (int i = 0; i < otherSatellitesCount; ++i) {
+            GpsSatellite otherSatellite = otherSatellites.valueAt(i);
+            int otherSatellitePrn = otherSatellite.getPrn();
+
+            int satellitesCount = mSatellites.size();
+            while (satelliteIndex < satellitesCount
+                    && mSatellites.valueAt(satelliteIndex).getPrn() < otherSatellitePrn) {
+                ++satelliteIndex;
+            }
+
+            if (satelliteIndex < mSatellites.size()) {
+                GpsSatellite satellite = mSatellites.valueAt(satelliteIndex);
+                if (satellite.getPrn() == otherSatellitePrn) {
+                    satellite.setStatus(otherSatellite);
+                } else {
+                    satellite = new GpsSatellite(otherSatellitePrn);
+                    satellite.setStatus(otherSatellite);
+                    mSatellites.put(otherSatellitePrn, satellite);
+                }
+            } else {
+                GpsSatellite satellite = new GpsSatellite(otherSatellitePrn);
+                satellite.setStatus(otherSatellite);
+                mSatellites.append(otherSatellitePrn, satellite);
+            }
+        }
     }
 
     void setTimeToFirstFix(int ttff) {
@@ -183,7 +214,7 @@
     }
 
     /**
-     * Returns the time required to receive the first fix since the most recent 
+     * Returns the time required to receive the first fix since the most recent
      * restart of the GPS engine.
      *
      * @return time to first fix in milliseconds
@@ -211,4 +242,12 @@
     public int getMaxSatellites() {
         return NUM_SATELLITES;
     }
+
+    private void clearSatellites() {
+        int satellitesCount = mSatellites.size();
+        for (int i = 0; i < satellitesCount; i++) {
+            GpsSatellite satellite = mSatellites.valueAt(i);
+            satellite.mValid = false;
+        }
+    }
 }
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index fcf222b..bf3387b 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -170,6 +170,9 @@
      * Converts a coordinate to a String representation. The outputType
      * may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
      * The coordinate must be a valid double between -180.0 and 180.0.
+     * This conversion is performed in a method that is dependent on the
+     * default locale, and so is not guaranteed to round-trip with
+     * {@link #convert(String)}.
      *
      * @throws IllegalArgumentException if coordinate is less than
      * -180.0, greater than 180.0, or is not a number.
@@ -217,7 +220,9 @@
     /**
      * Converts a String in one of the formats described by
      * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
-     * double.
+     * double. This conversion is performed in a locale agnostic
+     * method, and so is not guaranteed to round-trip with
+     * {@link #convert(double, int)}.
      *
      * @throws NullPointerException if coordinate is null
      * @throws IllegalArgumentException if the coordinate is not
diff --git a/location/tests/locationtests/src/android/location/GpsStatusTest.java b/location/tests/locationtests/src/android/location/GpsStatusTest.java
new file mode 100644
index 0000000..4808faf
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GpsStatusTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import junit.framework.TestCase;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link GpsStatus}.
+ */
+@SmallTest
+public class GpsStatusTest extends TestCase {
+
+    private static final int MAX_VALUE = 250;
+
+    private final Random mRandom = new Random();
+
+    private GpsStatus mStatus;
+    private int mCount;
+    private int[] mPrns;
+    private float[] mSnrs;
+    private float[] mElevations;
+    private float[] mAzimuth;
+    private int mEphemerisMask;
+    private int mAlmanacMask;
+    private int mUsedInFixMask;
+
+    public void setUp() throws Exception {
+        super.setUp();
+        mStatus = createGpsStatus();
+        generateSatellitesData(generateInt());
+    }
+
+    public void testEmptyGpsStatus() throws Exception {
+        verifyIsEmpty(mStatus);
+    }
+
+    public void testGpsStatusIterator() throws Exception {
+        generateSatellitesData(2);
+        setSatellites(mStatus);
+        Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
+        assertTrue("hasNext(1)", iterator.hasNext());
+        assertTrue("hasNext(1) does not overflow", iterator.hasNext());
+        GpsSatellite satellite1 = iterator.next();
+        assertNotNull("satellite", satellite1);
+        assertTrue("hasNext(2)", iterator.hasNext());
+        assertTrue("hasNext(2) does not overflow", iterator.hasNext());
+        GpsSatellite satellite2 = iterator.next();
+        assertNotNull("satellite", satellite2);
+        assertFalse("hasNext() no elements", iterator.hasNext());
+    }
+
+    public void testTtff() throws Exception {
+        int testTtff = generateInt();
+        set(mStatus, testTtff);
+        verifyTtff(mStatus, testTtff);
+    }
+
+    public void testCopyTtff() throws Exception {
+        int testTtff = generateInt();
+        verifyTtff(mStatus, 0);
+
+        GpsStatus otherStatus = createGpsStatus();
+        set(otherStatus, testTtff);
+        verifyTtff(otherStatus, testTtff);
+
+        set(mStatus, otherStatus);
+        verifyTtff(mStatus, testTtff);
+    }
+
+    public void testSetSatellites() throws Exception {
+        setSatellites(mStatus);
+        verifySatellites(mStatus);
+    }
+
+    public void testCopySatellites() throws Exception {
+        verifyIsEmpty(mStatus);
+
+        GpsStatus otherStatus = createGpsStatus();
+        setSatellites(otherStatus);
+        verifySatellites(otherStatus);
+
+        set(mStatus, otherStatus);
+        verifySatellites(mStatus);
+    }
+
+    public void testOverrideSatellites() throws Exception {
+        setSatellites(mStatus);
+        verifySatellites(mStatus);
+
+        GpsStatus otherStatus = createGpsStatus();
+        generateSatellitesData(mCount, true /* reusePrns */);
+        setSatellites(otherStatus);
+        verifySatellites(otherStatus);
+
+        set(mStatus, otherStatus);
+        verifySatellites(mStatus);
+    }
+
+    public void testAddSatellites() throws Exception {
+        int count = 10;
+        generateSatellitesData(count);
+        setSatellites(mStatus);
+        verifySatellites(mStatus);
+
+        GpsStatus otherStatus = createGpsStatus();
+        generateSatellitesData(count);
+        setSatellites(otherStatus);
+        verifySatellites(otherStatus);
+
+        set(mStatus, otherStatus);
+        verifySatellites(mStatus);
+    }
+
+    public void testAddMoreSatellites() throws Exception {
+        int count = 25;
+        generateSatellitesData(count);
+        setSatellites(mStatus);
+        verifySatellites(mStatus);
+
+        GpsStatus otherStatus = createGpsStatus();
+        generateSatellitesData(count * 2);
+        setSatellites(otherStatus);
+        verifySatellites(otherStatus);
+
+        set(mStatus, otherStatus);
+        verifySatellites(mStatus);
+    }
+
+    public void testAddLessSatellites() throws Exception {
+        int count = 25;
+        generateSatellitesData(count * 2);
+        setSatellites(mStatus);
+        verifySatellites(mStatus);
+
+        GpsStatus otherStatus = createGpsStatus();
+        generateSatellitesData(count);
+        setSatellites(otherStatus);
+        verifySatellites(otherStatus);
+
+        set(mStatus, otherStatus);
+        verifySatellites(mStatus);
+    }
+
+    private static void verifyIsEmpty(GpsStatus status) {
+        verifySatelliteCount(status, 0);
+        verifyTtff(status, 0);
+    }
+
+    private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
+        int satellites = 0;
+        for (GpsSatellite s : status.getSatellites()) {
+            ++satellites;
+        }
+        assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
+    }
+
+    private void verifySatellites(GpsStatus status) {
+        verifySatelliteCount(status, mCount);
+        verifySatellites(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask,
+                mAlmanacMask, mUsedInFixMask);
+    }
+
+    private static void verifySatellites(
+            GpsStatus status,
+            int count,
+            int[] prns,
+            float[] snrs,
+            float[] elevations,
+            float[] azimuth,
+            int ephemerisMask,
+            int almanacMask,
+            int usedInFixMask) {
+        for (int i = 0; i < count; ++i) {
+            int prn = prns[i];
+            GpsSatellite satellite = getSatellite(status, prn);
+            assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
+            assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), snrs[i], satellite.getSnr());
+            assertEquals(
+                    getSatelliteAssertInfo(i, prn, "Elevation"),
+                    elevations[i],
+                    satellite.getElevation());
+            assertEquals(
+                    getSatelliteAssertInfo(i, prn, "Azimuth"),
+                    azimuth[i],
+                    satellite.getAzimuth());
+            int prnShift = 1 << (prn - 1);
+            assertEquals(
+                    getSatelliteAssertInfo(i, prn, "ephemeris"),
+                    (ephemerisMask & prnShift) != 0,
+                    satellite.hasEphemeris());
+            assertEquals(
+                    getSatelliteAssertInfo(i, prn, "almanac"),
+                    (almanacMask & prnShift) != 0,
+                    satellite.hasAlmanac());
+            assertEquals(
+                    getSatelliteAssertInfo(i, prn, "usedInFix"),
+                    (usedInFixMask & prnShift) != 0,
+                    satellite.usedInFix());
+        }
+    }
+
+    private static void verifyTtff(GpsStatus status, int expectedTtff) {
+        assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
+    }
+
+    private static GpsStatus createGpsStatus() throws Exception {
+        Constructor<GpsStatus>  ctor = GpsStatus.class.getDeclaredConstructor();
+        ctor.setAccessible(true);
+        return ctor.newInstance();
+    }
+
+    private static void set(GpsStatus status, int ttff) throws Exception {
+        Class<?> statusClass = status.getClass();
+        Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
+        setTtff.setAccessible(true);
+        setTtff.invoke(status, ttff);
+    }
+
+    private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
+        Class<?> statusClass = status.getClass();
+        Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
+        setStatus.setAccessible(true);
+        setStatus.invoke(status, statusToSet);
+    }
+
+    private void setSatellites(GpsStatus status) throws Exception {
+        set(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
+                mUsedInFixMask);
+    }
+
+    private static void set(
+            GpsStatus status,
+            int count,
+            int[] prns,
+            float[] snrs,
+            float[] elevations,
+            float[] azimuth,
+            int ephemerisMask,
+            int almanacMask,
+            int usedInFixMask) throws Exception {
+        Class<?> statusClass = status.getClass();
+        Class<?> intClass = Integer.TYPE;
+        Class<?> floatArrayClass = Class.forName("[F");
+        Method setStatus = statusClass.getDeclaredMethod(
+                "setStatus",
+                intClass,
+                Class.forName("[I"),
+                floatArrayClass,
+                floatArrayClass,
+                floatArrayClass,
+                intClass,
+                intClass,
+                intClass);
+        setStatus.setAccessible(true);
+        setStatus.invoke(
+                status,
+                count,
+                prns,
+                snrs,
+                elevations,
+                azimuth,
+                ephemerisMask,
+                almanacMask,
+                usedInFixMask);
+    }
+
+    private int generateInt() {
+        return mRandom.nextInt(MAX_VALUE) + 1;
+    }
+
+    private int[] generateIntArray(int count) {
+        Set<Integer> generatedPrns = new HashSet<>();
+        int[] array = new int[count];
+        for(int i = 0; i < count; ++i) {
+            int generated;
+            do {
+                generated = generateInt();
+            } while (generatedPrns.contains(generated));
+            array[i] = generated;
+            generatedPrns.add(generated);
+        }
+        return array;
+    }
+
+    private float[] generateFloatArray(int count) {
+        float[] array = new float[count];
+        for(int i = 0; i < count; ++i) {
+            array[i] = generateInt();
+        }
+        return array;
+    }
+
+    private int generateMask(int[] prns) {
+        int mask = 0;
+        int prnsLength = prns.length;
+        for (int i = 0; i < prnsLength; ++i) {
+            if (mRandom.nextBoolean()) {
+                mask |= 1 << (prns[i] - 1);
+            }
+        }
+        return mask;
+    }
+
+    private void generateSatellitesData(int count) {
+        generateSatellitesData(count, false /* reusePrns */);
+    }
+
+    private void generateSatellitesData(int count, boolean reusePrns) {
+        mCount = count;
+        if (!reusePrns) {
+            mPrns = generateIntArray(count);
+        }
+        mSnrs = generateFloatArray(count);
+        mElevations = generateFloatArray(count);
+        mAzimuth = generateFloatArray(count);
+        mEphemerisMask = generateMask(mPrns);
+        mAlmanacMask = generateMask(mPrns);
+        mUsedInFixMask = generateMask(mPrns);
+    }
+
+    private static GpsSatellite getSatellite(GpsStatus status, int prn) {
+        for (GpsSatellite satellite : status.getSatellites()) {
+            if (satellite.getPrn() == prn) {
+                return satellite;
+            }
+        }
+        return null;
+    }
+
+    private static String getSatelliteAssertInfo(int index, int prn, String param) {
+        return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
+    }
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 489f552..ca242e4 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -709,7 +709,13 @@
         }
     }
 
-    /** @hide */
+    /**
+     * @hide
+     * Only use to get which stream type should be used for volume control, NOT for audio playback
+     * (all audio playback APIs are supposed to take AudioAttributes as input parameters)
+     * @param aa non-null AudioAttributes.
+     * @return a valid stream type for volume control that matches the attributes.
+     */
     public static int toLegacyStreamType(AudioAttributes aa) {
         // flags to stream type mapping
         if ((aa.getFlags() & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) {
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index b10736b..82da27d 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -36,12 +36,13 @@
     private final int mType;
     private final String mAddress;
 
-    AudioDevicePort(AudioHandle handle, int[] samplingRates, int[] channelMasks,
+    AudioDevicePort(AudioHandle handle, String deviceName,
+            int[] samplingRates, int[] channelMasks,
             int[] formats, AudioGain[] gains, int type, String address) {
         super(handle,
              (AudioManager.isInputDevice(type) == true)  ?
                         AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
-             samplingRates, channelMasks, formats, gains);
+             deviceName, samplingRates, channelMasks, formats, gains);
         mType = type;
         mAddress = address;
     }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9bcf3c8..07b19a4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -46,9 +46,9 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
-import java.util.HashMap;
 import java.util.ArrayList;
-
+import java.util.HashMap;
+import java.util.Iterator;
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -346,6 +346,31 @@
      */
     public static final int ADJUST_SAME = 0;
 
+    /**
+     * Mute the volume. Has no effect if the stream is already muted.
+     *
+     * @see #adjustVolume(int, int)
+     * @see #adjustStreamVolume(int, int, int)
+     */
+    public static final int ADJUST_MUTE = -100;
+
+    /**
+     * Unmute the volume. Has no effect if the stream is not muted.
+     *
+     * @see #adjustVolume(int, int)
+     * @see #adjustStreamVolume(int, int, int)
+     */
+    public static final int ADJUST_UNMUTE = 100;
+
+    /**
+     * Toggle the mute state. If muted the stream will be unmuted. If not muted
+     * the stream will be muted.
+     *
+     * @see #adjustVolume(int, int)
+     * @see #adjustStreamVolume(int, int, int)
+     */
+    public static final int ADJUST_TOGGLE_MUTE = 101;
+
     // Flags should be powers of 2!
 
     /**
@@ -777,13 +802,17 @@
      * screen is showing. Another example, if music is playing in the background
      * and a call is not active, the music stream will be adjusted.
      * <p>
-     * This method should only be used by applications that replace the platform-wide
-     * management of audio settings or the main telephony application.
-     * <p>This method has no effect if the device implements a fixed volume policy
+     * This method should only be used by applications that replace the
+     * platform-wide management of audio settings or the main telephony
+     * application.
+     * <p>
+     * This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
+     *
      * @param direction The direction to adjust the volume. One of
-     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
-     *            {@link #ADJUST_SAME}.
+     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+     *            {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+     *            {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
      * @param flags One or more flags.
      * @see #adjustSuggestedStreamVolume(int, int, int)
      * @see #adjustStreamVolume(int, int, int)
@@ -808,16 +837,20 @@
      * Adjusts the volume of the most relevant stream, or the given fallback
      * stream.
      * <p>
-     * This method should only be used by applications that replace the platform-wide
-     * management of audio settings or the main telephony application.
-     *
-     * <p>This method has no effect if the device implements a fixed volume policy
+     * This method should only be used by applications that replace the
+     * platform-wide management of audio settings or the main telephony
+     * application.
+     * <p>
+     * This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
+     *
      * @param direction The direction to adjust the volume. One of
-     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
-     *            {@link #ADJUST_SAME}.
+     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+     *            {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+     *            {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
      * @param suggestedStreamType The stream type that will be used if there
-     *            isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is valid here.
+     *            isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
+     *            valid here.
      * @param flags One or more flags.
      * @see #adjustVolume(int, int)
      * @see #adjustStreamVolume(int, int, int)
@@ -1088,77 +1121,81 @@
     }
 
     /**
-     * Solo or unsolo a particular stream. All other streams are muted.
+     * Solo or unsolo a particular stream.
      * <p>
-     * The solo command is protected against client process death: if a process
-     * with an active solo request on a stream dies, all streams that were muted
-     * because of this request will be unmuted automatically.
-     * <p>
-     * The solo requests for a given stream are cumulative: the AudioManager
-     * can receive several solo requests from one or more clients and the stream
-     * will be unsoloed only when the same number of unsolo requests are received.
-     * <p>
-     * For a better user experience, applications MUST unsolo a soloed stream
-     * in onPause() and solo is again in onResume() if appropriate.
-     * <p>This method has no effect if the device implements a fixed volume policy
-     * as indicated by {@link #isVolumeFixed()}.
+     * Do not use. This method has been deprecated and is now a no-op.
+     * {@link #requestAudioFocus} should be used for exclusive audio playback.
      *
      * @param streamType The stream to be soloed/unsoloed.
-     * @param state The required solo state: true for solo ON, false for solo OFF
-     *
+     * @param state The required solo state: true for solo ON, false for solo
+     *            OFF
      * @see #isVolumeFixed()
+     * @deprecated Do not use. If you need exclusive audio playback use
+     *             {@link #requestAudioFocus}.
      */
+    @Deprecated
     public void setStreamSolo(int streamType, boolean state) {
-        IAudioService service = getService();
-        try {
-            service.setStreamSolo(streamType, state, mICallBack);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in setStreamSolo", e);
-        }
+        Log.w(TAG, "setStreamSolo has been deprecated. Do not use.");
     }
 
     /**
      * Mute or unmute an audio stream.
      * <p>
-     * The mute command is protected against client process death: if a process
-     * with an active mute request on a stream dies, this stream will be unmuted
-     * automatically.
+     * This method should only be used by applications that replace the
+     * platform-wide management of audio settings or the main telephony
+     * application.
      * <p>
-     * The mute requests for a given stream are cumulative: the AudioManager
-     * can receive several mute requests from one or more clients and the stream
-     * will be unmuted only when the same number of unmute requests are received.
-     * <p>
-     * For a better user experience, applications MUST unmute a muted stream
-     * in onPause() and mute is again in onResume() if appropriate.
-     * <p>
-     * This method should only be used by applications that replace the platform-wide
-     * management of audio settings or the main telephony application.
-     * <p>This method has no effect if the device implements a fixed volume policy
+     * This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
+     * <p>
+     * This method was deprecated in API level 22. Prior to API level 22 this
+     * method had significantly different behavior and should be used carefully.
+     * The following applies only to pre-22 platforms:
+     * <ul>
+     * <li>The mute command is protected against client process death: if a
+     * process with an active mute request on a stream dies, this stream will be
+     * unmuted automatically.</li>
+     * <li>The mute requests for a given stream are cumulative: the AudioManager
+     * can receive several mute requests from one or more clients and the stream
+     * will be unmuted only when the same number of unmute requests are
+     * received.</li>
+     * <li>For a better user experience, applications MUST unmute a muted stream
+     * in onPause() and mute is again in onResume() if appropriate.</li>
+     * </ul>
      *
      * @param streamType The stream to be muted/unmuted.
-     * @param state The required mute state: true for mute ON, false for mute OFF
-     *
+     * @param state The required mute state: true for mute ON, false for mute
+     *            OFF
      * @see #isVolumeFixed()
+     * @deprecated Use {@link #adjustStreamVolume(int, int, int)} with
+     *             {@link #ADJUST_MUTE} or {@link #ADJUST_UNMUTE} instead.
      */
+    @Deprecated
     public void setStreamMute(int streamType, boolean state) {
-        IAudioService service = getService();
-        try {
-            service.setStreamMute(streamType, state, mICallBack);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in setStreamMute", e);
+        Log.w(TAG, "setStreamMute is deprecated. adjustStreamVolume should be used instead.");
+        int direction = state ? ADJUST_MUTE : ADJUST_UNMUTE;
+        if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+            adjustSuggestedStreamVolume(direction, streamType, 0);
+        } else {
+            adjustStreamVolume(streamType, direction, 0);
         }
     }
 
     /**
-     * get stream mute state.
+     * Returns the current mute state for a particular stream.
      *
-     * @hide
+     * @param streamType The stream to get mute state for.
+     * @return The mute state for the given stream.
+     * @see #adjustStreamVolume(int, int, int)
      */
     public boolean isStreamMute(int streamType) {
         IAudioService service = getService();
         try {
-            return service.isStreamMute(streamType);
+            if (mUseMasterVolume) {
+                return service.isMasterMute();
+            } else {
+                return service.isStreamMute(streamType);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in isStreamMute", e);
             return false;
@@ -1166,29 +1203,6 @@
     }
 
     /**
-     * set master mute state.
-     *
-     * @hide
-     */
-    public void setMasterMute(boolean state) {
-        setMasterMute(state, FLAG_SHOW_UI);
-    }
-
-    /**
-     * set master mute state with optional flags.
-     *
-     * @hide
-     */
-    public void setMasterMute(boolean state, int flags) {
-        IAudioService service = getService();
-        try {
-            service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in setMasterMute", e);
-        }
-    }
-
-    /**
      * get master mute state.
      *
      * @hide
@@ -3189,10 +3203,10 @@
      * @param name   device name
      * {@hide}
      */
-    public void setWiredDeviceConnectionState(int device, int state, String name) {
+    public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
         IAudioService service = getService();
         try {
-            service.setWiredDeviceConnectionState(device, state, name);
+            service.setWiredDeviceConnectionState(type, state, address, name);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setWiredDeviceConnectionState "+e);
         }
@@ -3588,11 +3602,13 @@
                     newPorts.clear();
                     status = AudioSystem.listAudioPorts(newPorts, portGeneration);
                     if (status != SUCCESS) {
+                        Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
                         return status;
                     }
                     newPatches.clear();
                     status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
                     if (status != SUCCESS) {
+                        Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
                         return status;
                     }
                 } while (patchGeneration[0] != portGeneration[0]);
@@ -3601,20 +3617,35 @@
                     for (int j = 0; j < newPatches.get(i).sources().length; j++) {
                         AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
                                                                    newPorts);
-                        if (portCfg == null) {
-                            return ERROR;
-                        }
                         newPatches.get(i).sources()[j] = portCfg;
                     }
                     for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
                         AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
                                                                    newPorts);
-                        if (portCfg == null) {
-                            return ERROR;
-                        }
                         newPatches.get(i).sinks()[j] = portCfg;
                     }
                 }
+                for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) {
+                    AudioPatch newPatch = i.next();
+                    boolean hasInvalidPort = false;
+                    for (AudioPortConfig portCfg : newPatch.sources()) {
+                        if (portCfg == null) {
+                            hasInvalidPort = true;
+                            break;
+                        }
+                    }
+                    for (AudioPortConfig portCfg : newPatch.sinks()) {
+                        if (portCfg == null) {
+                            hasInvalidPort = true;
+                            break;
+                        }
+                    }
+                    if (hasInvalidPort) {
+                        // Temporarily remove patches with invalid ports. One who created the patch
+                        // is responsible for dealing with the port change.
+                        i.remove();
+                    }
+                }
 
                 sAudioPortsCached = newPorts;
                 sAudioPatchesCached = newPatches;
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 616bdd1..873c142 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -41,9 +41,6 @@
     public abstract void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
             int uid);
 
-    public abstract void setMasterMuteForUid(boolean state, int flags, String callingPackage,
-            IBinder cb, int uid);
-
     public abstract void setRingerModeDelegate(RingerModeDelegate delegate);
 
     public abstract int getRingerModeInternal();
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
index 1500a43..9fac8d1 100644
--- a/media/java/android/media/AudioMixPort.java
+++ b/media/java/android/media/AudioMixPort.java
@@ -26,9 +26,10 @@
 
 public class AudioMixPort extends AudioPort {
 
-    AudioMixPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+    AudioMixPort(AudioHandle handle, int role, String deviceName,
+            int[] samplingRates, int[] channelMasks,
             int[] formats, AudioGain[] gains) {
-        super(handle, role, samplingRates, channelMasks, formats, gains);
+        super(handle, role, deviceName, samplingRates, channelMasks, formats, gains);
     }
 
     /**
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index 1ab7e89..b046791 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -15,7 +15,7 @@
  */
 
 package android.media;
-
+import android.util.Slog;
 
 /**
  * An audio port is a node of the audio framework or hardware that can be connected to or
@@ -37,6 +37,7 @@
  * @hide
  */
 public class AudioPort {
+    private static final String TAG = "AudioPort";
 
     /**
      * For use by the audio framework.
@@ -68,16 +69,20 @@
 
     AudioHandle mHandle;
     protected final int mRole;
+    private final String mName;
     private final int[] mSamplingRates;
     private final int[] mChannelMasks;
     private final int[] mFormats;
     private final AudioGain[] mGains;
     private AudioPortConfig mActiveConfig;
 
-    AudioPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+    AudioPort(AudioHandle handle, int role, String name,
+            int[] samplingRates, int[] channelMasks,
             int[] formats, AudioGain[] gains) {
+
         mHandle = handle;
         mRole = role;
+        mName = name;
         mSamplingRates = samplingRates;
         mChannelMasks = channelMasks;
         mFormats = formats;
@@ -96,6 +101,14 @@
     }
 
     /**
+     * Get the human-readable name of this port. Perhaps an internal
+     * designation or an physical device.
+     */
+    public String name() {
+        return mName;
+    }
+
+    /**
      * Get the list of supported sampling rates
      * Empty array if sampling rate is not relevant for this audio port
      */
diff --git a/media/java/android/media/AudioRoutesInfo.java b/media/java/android/media/AudioRoutesInfo.java
index df9fc06..3e0ec07 100644
--- a/media/java/android/media/AudioRoutesInfo.java
+++ b/media/java/android/media/AudioRoutesInfo.java
@@ -30,6 +30,7 @@
     static final int MAIN_HEADPHONES = 1<<1;
     static final int MAIN_DOCK_SPEAKERS = 1<<2;
     static final int MAIN_HDMI = 1<<3;
+    static final int MAIN_USB = 1<<4;
 
     CharSequence mBluetoothName;
     int mMainType = MAIN_SPEAKER;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 766a837..3e91e4c 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -153,11 +153,11 @@
     private final AppOpsManager mAppOps;
 
     // the platform has no specific capabilities
-    private static final int PLATFORM_DEFAULT = 0;
+    public static final int PLATFORM_DEFAULT = 0;
     // the platform is voice call capable (a phone)
-    private static final int PLATFORM_VOICE = 1;
+    public static final int PLATFORM_VOICE = 1;
     // the platform is a television or a set-top box
-    private static final int PLATFORM_TELEVISION = 2;
+    public static final int PLATFORM_TELEVISION = 2;
     // the platform type affects volume and silent mode behavior
     private final int mPlatformType;
 
@@ -356,6 +356,12 @@
             "STREAM_TTS"
     };
 
+    public static final int DEFAULT_MUTE_STREAMS_AFFECTED =
+            (1 << AudioSystem.STREAM_MUSIC) |
+            (1 << AudioSystem.STREAM_RING) |
+            (1 << AudioSystem.STREAM_NOTIFICATION) |
+            (1 << AudioSystem.STREAM_SYSTEM);
+
     private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
         public void onError(int error) {
             switch (error) {
@@ -536,6 +542,20 @@
 
     private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
 
+    // Intent "extra" data keys.
+    public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
+    public static final String CONNECT_INTENT_KEY_STATE = "state";
+    public static final String CONNECT_INTENT_KEY_ADDRESS = "address";
+    public static final String CONNECT_INTENT_KEY_HAS_PLAYBACK = "hasPlayback";
+    public static final String CONNECT_INTENT_KEY_HAS_CAPTURE = "hasCapture";
+    public static final String CONNECT_INTENT_KEY_HAS_MIDI = "hasMIDI";
+    public static final String CONNECT_INTENT_KEY_DEVICE_CLASS = "class";
+
+    // Defines the format for the connection "address" for ALSA devices
+    public static String makeAlsaAddressString(int card, int device) {
+        return "card=" + card + ";device=" + device + ";";
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Construction
     ///////////////////////////////////////////////////////////////////////////
@@ -546,15 +566,7 @@
         mContentResolver = context.getContentResolver();
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
 
-        if (mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_voice_capable)) {
-            mPlatformType = PLATFORM_VOICE;
-        } else if (context.getPackageManager().hasSystemFeature(
-                                                            PackageManager.FEATURE_LEANBACK)) {
-            mPlatformType = PLATFORM_TELEVISION;
-        } else {
-            mPlatformType = PLATFORM_DEFAULT;
-        }
+        mPlatformType = getPlatformType(context);
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
@@ -667,6 +679,26 @@
         LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
     }
 
+    /**
+     * Return the platform type that this is running on. One of:
+     * <ul>
+     * <li>{@link #PLATFORM_VOICE}</li>
+     * <li>{@link #PLATFORM_TELEVISION}</li>
+     * <li>{@link #PLATFORM_DEFAULT}</li>
+     * </ul>
+     */
+    public static int getPlatformType(Context context) {
+        if (context.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable)) {
+            return PLATFORM_VOICE;
+        } else if (context.getPackageManager().hasSystemFeature(
+                                                            PackageManager.FEATURE_LEANBACK)) {
+            return PLATFORM_TELEVISION;
+        } else {
+            return PLATFORM_DEFAULT;
+        }
+    }
+
     public void systemReady() {
         sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
                 0, 0, null, 0);
@@ -748,7 +780,7 @@
                                     setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
                 }
                 // apply stream volume
-                if (!mStreamStates[streamType].isMuted_syncVSS()) {
+                if (!mStreamStates[streamType].mIsMuted) {
                     mStreamStates[streamType].applyAllVolumes();
                 }
             }
@@ -899,11 +931,8 @@
         }
 
         mMuteAffectedStreams = System.getIntForUser(cr,
-                System.MUTE_STREAMS_AFFECTED,
-                ((1 << AudioSystem.STREAM_MUSIC)|
-                 (1 << AudioSystem.STREAM_RING)|
-                 (1 << AudioSystem.STREAM_SYSTEM)),
-                 UserHandle.USER_CURRENT);
+                System.MUTE_STREAMS_AFFECTED, DEFAULT_MUTE_STREAMS_AFFECTED,
+                UserHandle.USER_CURRENT);
 
         boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
                                                   0, UserHandle.USER_CURRENT) == 1;
@@ -970,6 +999,7 @@
         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType
                 + ", flags=" + flags);
         int streamType;
+        boolean isMute = isMuteAdjust(direction);
         if (mVolumeControlStream != -1) {
             streamType = mVolumeControlStream;
         } else {
@@ -984,7 +1014,8 @@
         }
 
         // For notifications/ring, show the ui before making any adjustments
-        if (mVolumeController.suppressAdjustment(resolvedStream, flags)) {
+        // Don't suppress mute/unmute requests
+        if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
             direction = 0;
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
             flags &= ~AudioManager.FLAG_VIBRATE;
@@ -1011,10 +1042,17 @@
         ensureValidDirection(direction);
         ensureValidStreamType(streamType);
 
+        boolean isMuteAdjust = isMuteAdjust(direction);
+
         // use stream type alias here so that streams with same alias have the same behavior,
         // including with regard to silent mode control (e.g the use of STREAM_RING below and in
         // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
         int streamTypeAlias = mStreamVolumeAlias[streamType];
+
+        if (isMuteAdjust && !isStreamAffectedByMute(streamTypeAlias)) {
+            return;
+        }
+
         VolumeStreamState streamState = mStreamStates[streamTypeAlias];
 
         final int device = getDeviceForStream(streamTypeAlias);
@@ -1100,13 +1138,37 @@
                 }
             }
 
-            if ((direction == AudioManager.ADJUST_RAISE) &&
+            if (isMuteAdjust) {
+                boolean state;
+                if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
+                    state = !streamState.mIsMuted;
+                } else {
+                    state = direction == AudioManager.ADJUST_MUTE;
+                }
+                if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
+                    setSystemAudioMute(state);
+                }
+                for (int stream = 0; stream < mStreamStates.length; stream++) {
+                    if (streamTypeAlias == mStreamVolumeAlias[stream]) {
+                        mStreamStates[stream].mute(state);
+
+                        Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+                        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, stream);
+                        intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
+                        sendBroadcastToAll(intent);
+                    }
+                }
+            } else if ((direction == AudioManager.ADJUST_RAISE) &&
                     !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
-                Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
+                Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
                 mVolumeController.postDisplaySafeVolumeWarning(flags);
-            } else if (streamState.adjustIndex(direction * step, device)) {
-                // Post message to set system volume (it in turn will post a message
-                // to persist). Do not change volume if stream is muted.
+            } else if (streamState.adjustIndex(direction * step, device) || streamState.mIsMuted) {
+                // Post message to set system volume (it in turn will post a
+                // message to persist).
+                if (streamState.mIsMuted) {
+                    // Unmute the stream if it was previously muted
+                    streamState.mute(false);
+                }
                 sendMsg(mAudioHandler,
                         MSG_SET_DEVICE_VOLUME,
                         SENDMSG_QUEUE,
@@ -1116,7 +1178,7 @@
                         0);
             }
 
-            // Check if volume update should be send to Hdmi system audio.
+            // Check if volume update should be sent to Hdmi system audio.
             int newIndex = mStreamStates[streamType].getIndex(device);
             if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                 setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
@@ -1129,7 +1191,7 @@
                             oldIndex != newIndex) {
                         synchronized (mHdmiPlaybackClient) {
                             int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
-                                                               KeyEvent.KEYCODE_VOLUME_UP;
+                                    KeyEvent.KEYCODE_VOLUME_UP;
                             mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
                             mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
                         }
@@ -1172,6 +1234,10 @@
         if (mUseFixedVolume) {
             return;
         }
+        if (isMuteAdjust(steps)) {
+            setMasterMuteInternal(steps, flags, callingPackage, uid);
+            return;
+        }
         ensureValidSteps(steps);
         int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
         int delta = 0;
@@ -1500,46 +1566,6 @@
         }
     }
 
-    /** @see AudioManager#setStreamSolo(int, boolean) */
-    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
-        if (mUseFixedVolume) {
-            return;
-        }
-        int streamAlias = mStreamVolumeAlias[streamType];
-        for (int stream = 0; stream < mStreamStates.length; stream++) {
-            if (!isStreamAffectedByMute(streamAlias) || streamAlias == mStreamVolumeAlias[stream]) {
-                continue;
-            }
-            mStreamStates[stream].mute(cb, state);
-         }
-    }
-
-    /** @see AudioManager#setStreamMute(int, boolean) */
-    public void setStreamMute(int streamType, boolean state, IBinder cb) {
-        if (mUseFixedVolume) {
-            return;
-        }
-        if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-            streamType = getActiveStreamType(streamType);
-        }
-        int streamAlias = mStreamVolumeAlias[streamType];
-        if (isStreamAffectedByMute(streamAlias)) {
-            if (streamAlias == AudioSystem.STREAM_MUSIC) {
-                setSystemAudioMute(state);
-            }
-            for (int stream = 0; stream < mStreamStates.length; stream++) {
-                if (streamAlias == mStreamVolumeAlias[stream]) {
-                    mStreamStates[stream].mute(cb, state);
-
-                    Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
-                    intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, stream);
-                    intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
-                    sendBroadcastToAll(intent);
-                }
-            }
-        }
-    }
-
     private void setSystemAudioMute(boolean state) {
         if (mHdmiManager == null || mHdmiTvClient == null) return;
         synchronized (mHdmiManager) {
@@ -1561,7 +1587,7 @@
             streamType = getActiveStreamType(streamType);
         }
         synchronized (VolumeStreamState.class) {
-            return mStreamStates[streamType].isMuted_syncVSS();
+            return mStreamStates[streamType].mIsMuted;
         }
     }
 
@@ -1665,20 +1691,17 @@
         }
     }
 
-    /** @see AudioManager#setMasterMute(boolean, int) */
-    public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
-        setMasterMuteInternal(state, flags, callingPackage, cb, Binder.getCallingUid());
-    }
-
-    private void setMasterMuteInternal(boolean state, int flags, String callingPackage, IBinder cb,
-            int uid) {
-        if (mUseFixedVolume) {
-            return;
-        }
+    private void setMasterMuteInternal(int adjust, int flags, String callingPackage, int uid) {
         if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
                 != AppOpsManager.MODE_ALLOWED) {
             return;
         }
+        boolean state;
+        if (adjust == AudioManager.ADJUST_TOGGLE_MUTE) {
+            state = !AudioSystem.getMasterMute();
+        } else {
+            state = adjust == AudioManager.ADJUST_MUTE;
+        }
         if (state != AudioSystem.getMasterMute()) {
             setSystemAudioMute(state);
             AudioSystem.setMasterMute(state);
@@ -1714,7 +1737,7 @@
             int index = mStreamStates[streamType].getIndex(device);
 
             // by convention getStreamVolume() returns 0 when a stream is muted.
-            if (mStreamStates[streamType].isMuted_syncVSS()) {
+            if (mStreamStates[streamType].mIsMuted) {
                 index = 0;
             }
             if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
@@ -1934,11 +1957,11 @@
                         }
                     }
                 }
-                mStreamStates[streamType].mute(null, false);
+                mStreamStates[streamType].mute(false);
                 mRingerModeMutedStreams &= ~(1 << streamType);
             } else {
                 // mute
-                mStreamStates[streamType].mute(null, true);
+                mStreamStates[streamType].mute(true);
                 mRingerModeMutedStreams |= (1 << streamType);
             }
         }
@@ -2426,13 +2449,9 @@
             streamState.readSettings();
             synchronized (VolumeStreamState.class) {
                 // unmute stream that was muted but is not affect by mute anymore
-                if (streamState.isMuted_syncVSS() && ((!isStreamAffectedByMute(streamType) &&
+                if (streamState.mIsMuted && ((!isStreamAffectedByMute(streamType) &&
                         !isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) {
-                    int size = streamState.mDeathHandlers.size();
-                    for (int i = 0; i < size; i++) {
-                        streamState.mDeathHandlers.get(i).mMuteCount = 1;
-                        streamState.mDeathHandlers.get(i).mute_syncVSS(false);
-                    }
+                    streamState.mIsMuted = false;
                 }
             }
         }
@@ -3221,8 +3240,16 @@
     }
 
     private void ensureValidDirection(int direction) {
-        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
-            throw new IllegalArgumentException("Bad direction " + direction);
+        switch (direction) {
+            case AudioManager.ADJUST_LOWER:
+            case AudioManager.ADJUST_RAISE:
+            case AudioManager.ADJUST_SAME:
+            case AudioManager.ADJUST_MUTE:
+            case AudioManager.ADJUST_UNMUTE:
+            case AudioManager.ADJUST_TOGGLE_MUTE:
+                break;
+            default:
+                throw new IllegalArgumentException("Bad direction " + direction);
         }
     }
 
@@ -3238,6 +3265,11 @@
         }
     }
 
+    private boolean isMuteAdjust(int adjust) {
+        return adjust == AudioManager.ADJUST_MUTE || adjust == AudioManager.ADJUST_UNMUTE
+                || adjust == AudioManager.ADJUST_TOGGLE_MUTE;
+    }
+
     private boolean isInCommunication() {
         boolean IsInCall = false;
 
@@ -3418,14 +3450,32 @@
         return device;
     }
 
-    public void setWiredDeviceConnectionState(int device, int state, String name) {
+    /*
+     * A class just for packaging up a set of connection parameters.
+     */
+    private class WiredDeviceConnectionState {
+        public int mType;
+        public int mState;
+        public String mAddress;
+        public String mName;
+
+        public WiredDeviceConnectionState(int type, int state, String address, String name) {
+            mType = type;
+            mState = state;
+            mAddress = address;
+            mName = name;
+        }
+    }
+
+    public void setWiredDeviceConnectionState(int type, int state, String address,
+            String name) {
         synchronized (mConnectedDevices) {
-            int delay = checkSendBecomingNoisyIntent(device, state);
+            int delay = checkSendBecomingNoisyIntent(type, state);
             queueMsgUnderWakeLock(mAudioHandler,
                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
-                    device,
-                    state,
-                    name,
+                    0,
+                    0,
+                    new WiredDeviceConnectionState(type, state, address, name),
                     delay);
         }
     }
@@ -3467,11 +3517,11 @@
     public class VolumeStreamState {
         private final int mStreamType;
 
+        private boolean mIsMuted;
         private String mVolumeIndexSettingName;
         private int mIndexMax;
         private final ConcurrentHashMap<Integer, Integer> mIndex =
                                             new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
-        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
 
         private VolumeStreamState(String settingName, int streamType) {
 
@@ -3482,9 +3532,6 @@
             AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
             mIndexMax *= 10;
 
-            // mDeathHandlers must be created before calling readSettings()
-            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
-
             readSettings();
         }
 
@@ -3549,7 +3596,7 @@
         // must be called while synchronized VolumeStreamState.class
         public void applyDeviceVolume_syncVSS(int device) {
             int index;
-            if (isMuted_syncVSS()) {
+            if (mIsMuted) {
                 index = 0;
             } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
                     || ((device & mFullVolumeDevices) != 0)) {
@@ -3565,7 +3612,7 @@
                 // apply default volume first: by convention this will reset all
                 // devices volumes in audio policy manager to the supplied value
                 int index;
-                if (isMuted_syncVSS()) {
+                if (mIsMuted) {
                     index = 0;
                 } else {
                     index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
@@ -3578,7 +3625,7 @@
                     Map.Entry entry = (Map.Entry)i.next();
                     int device = ((Integer)entry.getKey()).intValue();
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
-                        if (isMuted_syncVSS()) {
+                        if (mIsMuted) {
                             index = 0;
                         } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                                 mAvrcpAbsVolSupported)
@@ -3688,14 +3735,20 @@
             }
         }
 
-        public void mute(IBinder cb, boolean state) {
+        public void mute(boolean state) {
             synchronized (VolumeStreamState.class) {
-                VolumeDeathHandler handler = getDeathHandler_syncVSS(cb, state);
-                if (handler == null) {
-                    Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
-                    return;
+                if (state != mIsMuted) {
+                    mIsMuted = state;
+                    // Set the new mute volume. This propagates the values to
+                    // the audio system, otherwise the volume won't be changed
+                    // at the lower level.
+                    sendMsg(mAudioHandler,
+                            MSG_SET_ALL_VOLUMES,
+                            SENDMSG_QUEUE,
+                            0,
+                            0,
+                            this, 0);
                 }
-                handler.mute_syncVSS(state);
             }
         }
 
@@ -3733,117 +3786,9 @@
             return index;
         }
 
-        private class VolumeDeathHandler implements IBinder.DeathRecipient {
-            private IBinder mICallback; // To be notified of client's death
-            private int mMuteCount; // Number of active mutes for this client
-
-            VolumeDeathHandler(IBinder cb) {
-                mICallback = cb;
-            }
-
-            // must be called while synchronized VolumeStreamState.class
-            public void mute_syncVSS(boolean state) {
-                boolean updateVolume = false;
-                if (state) {
-                    if (mMuteCount == 0) {
-                        // Register for client death notification
-                        try {
-                            // mICallback can be 0 if muted by AudioService
-                            if (mICallback != null) {
-                                mICallback.linkToDeath(this, 0);
-                            }
-                            VolumeStreamState.this.mDeathHandlers.add(this);
-                            // If the stream is not yet muted by any client, set level to 0
-                            if (!VolumeStreamState.this.isMuted_syncVSS()) {
-                                updateVolume = true;
-                            }
-                        } catch (RemoteException e) {
-                            // Client has died!
-                            binderDied();
-                            return;
-                        }
-                    } else {
-                        Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
-                    }
-                    mMuteCount++;
-                } else {
-                    if (mMuteCount == 0) {
-                        Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
-                    } else {
-                        mMuteCount--;
-                        if (mMuteCount == 0) {
-                            // Unregister from client death notification
-                            VolumeStreamState.this.mDeathHandlers.remove(this);
-                            // mICallback can be 0 if muted by AudioService
-                            if (mICallback != null) {
-                                mICallback.unlinkToDeath(this, 0);
-                            }
-                            if (!VolumeStreamState.this.isMuted_syncVSS()) {
-                                updateVolume = true;
-                            }
-                        }
-                    }
-                }
-                if (updateVolume) {
-                    sendMsg(mAudioHandler,
-                            MSG_SET_ALL_VOLUMES,
-                            SENDMSG_QUEUE,
-                            0,
-                            0,
-                            VolumeStreamState.this, 0);
-                }
-            }
-
-            public void binderDied() {
-                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
-                synchronized (VolumeStreamState.class) {
-                    if (mMuteCount != 0) {
-                        // Reset all active mute requests from this client.
-                        mMuteCount = 1;
-                        mute_syncVSS(false);
-                    }
-                }
-            }
-        }
-
-        private int muteCount() {
-            int count = 0;
-            int size = mDeathHandlers.size();
-            for (int i = 0; i < size; i++) {
-                count += mDeathHandlers.get(i).mMuteCount;
-            }
-            return count;
-        }
-
-        // must be called while synchronized VolumeStreamState.class
-        private boolean isMuted_syncVSS() {
-            return muteCount() != 0;
-        }
-
-        // must be called while synchronized VolumeStreamState.class
-        private VolumeDeathHandler getDeathHandler_syncVSS(IBinder cb, boolean state) {
-            VolumeDeathHandler handler;
-            int size = mDeathHandlers.size();
-            for (int i = 0; i < size; i++) {
-                handler = mDeathHandlers.get(i);
-                if (cb == handler.mICallback) {
-                    return handler;
-                }
-            }
-            // If this is the first mute request for this client, create a new
-            // client death handler. Otherwise, it is an out of sequence unmute request.
-            if (state) {
-                handler = new VolumeDeathHandler(cb);
-            } else {
-                Log.w(TAG, "stream was not muted by this client");
-                handler = null;
-            }
-            return handler;
-        }
-
         private void dump(PrintWriter pw) {
-            pw.print("   Mute count: ");
-            pw.println(muteCount());
+            pw.print("   Muted: ");
+            pw.println(mIsMuted);
             pw.print("   Max: ");
             pw.println((mIndexMax + 5) / 10);
             pw.print("   Current: ");
@@ -4262,7 +4207,8 @@
                             AudioSystem.setDeviceConnectionState(
                                                             ((Integer)device.getKey()).intValue(),
                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
-                                                            (String)device.getValue());
+                                                            (String)device.getValue(),
+                                                            "unknown-device");
                         }
                     }
                     // Restore call state
@@ -4364,8 +4310,12 @@
                     break;
 
                 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
-                    onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
-                    mAudioEventWakeLock.release();
+                    {   WiredDeviceConnectionState connectState =
+                            (WiredDeviceConnectionState)msg.obj;
+                        onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
+                                connectState.mAddress, connectState.mName);
+                        mAudioEventWakeLock.release();
+                    }
                     break;
 
                 case MSG_SET_A2DP_SRC_CONNECTION_STATE:
@@ -4478,7 +4428,8 @@
         setBluetoothA2dpOnInt(true);
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE,
-                address);
+                address,
+                "a2dp-device");
         // Reset A2DP suspend state each time a new sink is connected
         AudioSystem.setParameters("A2dpSuspended=false");
         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
@@ -4496,7 +4447,8 @@
         }
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
-                address);
+                address,
+                "a2dp-device");
         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
         synchronized (mCurAudioRoutes) {
             // Remove A2DP routes as well
@@ -4525,7 +4477,8 @@
     private void makeA2dpSrcAvailable(String address) {
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE,
-                address);
+                address,
+                "a2dp-device");
         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP),
                 address);
     }
@@ -4534,7 +4487,8 @@
     private void makeA2dpSrcUnavailable(String address) {
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
-                address);
+                address,
+                "a2dp-device");
         mConnectedDevices.remove(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
     }
 
@@ -4650,22 +4604,26 @@
         }
     }
 
-    private boolean handleDeviceConnection(boolean connected, int device, String params) {
+    private boolean handleDeviceConnection(boolean connect, int device, String address, String deviceName) {
+        Slog.i(TAG, "handleDeviceConnection(" + connect +
+                " dev:" + Integer.toHexString(device) +
+                " address:" + address + 
+                " name:" + deviceName + ")");
         synchronized (mConnectedDevices) {
             boolean isConnected = (mConnectedDevices.containsKey(device) &&
-                    (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
+                    (address.isEmpty() || mConnectedDevices.get(device).equals(address)));
 
-            if (isConnected && !connected) {
+            if (isConnected && !connect) {
                 AudioSystem.setDeviceConnectionState(device,
                                               AudioSystem.DEVICE_STATE_UNAVAILABLE,
-                                              mConnectedDevices.get(device));
+                                              address, deviceName);
                  mConnectedDevices.remove(device);
                  return true;
-            } else if (!isConnected && connected) {
+            } else if (!isConnected && connect) {
                  AudioSystem.setDeviceConnectionState(device,
                                                       AudioSystem.DEVICE_STATE_AVAILABLE,
-                                                      params);
-                 mConnectedDevices.put(new Integer(device), params);
+                                                      address, deviceName);
+                 mConnectedDevices.put(new Integer(device), address);
                  return true;
             }
         }
@@ -4711,19 +4669,25 @@
             synchronized (mLastDeviceConnectMsgTime) {
                 long time = SystemClock.uptimeMillis();
                 if (mLastDeviceConnectMsgTime > time) {
-                    delay = (int)(mLastDeviceConnectMsgTime - time);
+                    delay = (int)(mLastDeviceConnectMsgTime - time) + 30;
                 }
             }
         }
         return delay;
     }
 
-    private void sendDeviceConnectionIntent(int device, int state, String name)
+    private void sendDeviceConnectionIntent(int device, int state, String address, String deviceName)
     {
+        Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device) +
+                " state:0x" + Integer.toHexString(state) +
+                " address:" + address +
+                " name:" + deviceName + ");");
         Intent intent = new Intent();
 
-        intent.putExtra("state", state);
-        intent.putExtra("name", name);
+        intent.putExtra(CONNECT_INTENT_KEY_STATE, state);
+        intent.putExtra(CONNECT_INTENT_KEY_ADDRESS, address);
+        intent.putExtra(CONNECT_INTENT_KEY_PORT_NAME, deviceName);
+
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
 
         int connType = 0;
@@ -4742,6 +4706,8 @@
                 device == AudioSystem.DEVICE_OUT_HDMI_ARC) {
             connType = AudioRoutesInfo.MAIN_HDMI;
             configureHdmiPlugIntent(intent, state);
+        } else if (device == AudioSystem.DEVICE_OUT_USB_DEVICE) {
+            connType = AudioRoutesInfo.MAIN_USB;
         }
 
         synchronized (mCurAudioRoutes) {
@@ -4768,8 +4734,14 @@
         }
     }
 
-    private void onSetWiredDeviceConnectionState(int device, int state, String name)
+    private void onSetWiredDeviceConnectionState(int device, int state, String address,
+            String deviceName)
     {
+        Slog.i(TAG, "onSetWiredDeviceConnectionState(dev:" + Integer.toHexString(device)
+                + " state:" + Integer.toHexString(state)
+                + " address:" + address
+                + " deviceName:" + deviceName + ");");
+
         synchronized (mConnectedDevices) {
             if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
@@ -4779,7 +4751,7 @@
             boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) ||
                             (((device & AudioSystem.DEVICE_BIT_IN) != 0) &&
                              ((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0));
-            handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
+            handleDeviceConnection(state == 1, device, address, deviceName);
             if (state != 0) {
                 if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
@@ -4817,8 +4789,8 @@
                     }
                 }
             }
-            if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
-                sendDeviceConnectionIntent(device, state, name);
+            if (!isUsb && device != AudioSystem.DEVICE_IN_WIRED_HEADSET) {
+                sendDeviceConnectionIntent(device, state, address, deviceName);
             }
         }
     }
@@ -4942,8 +4914,9 @@
                 }
 
                 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
-                boolean success = handleDeviceConnection(connected, outDevice, address) &&
-                                      handleDeviceConnection(connected, inDevice, address);
+                boolean success =
+                    handleDeviceConnection(connected, outDevice, address, "Bluetooth Headset") &&
+                    handleDeviceConnection(connected, inDevice, address, "Bluetooth Headset");
                 if (success) {
                     synchronized (mScoClients) {
                         if (connected) {
@@ -5648,7 +5621,10 @@
                     Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
         }
 
-        public boolean suppressAdjustment(int resolvedStream, int flags) {
+        public boolean suppressAdjustment(int resolvedStream, int flags, boolean isMute) {
+            if (isMute) {
+                return false;
+            }
             boolean suppress = false;
             if (resolvedStream == AudioSystem.STREAM_RING && mController != null) {
                 final long now = SystemClock.uptimeMillis();
@@ -5801,12 +5777,6 @@
         public void setRingerModeInternal(int ringerMode, String caller) {
             AudioService.this.setRingerModeInternal(ringerMode, caller);
         }
-
-        @Override
-        public void setMasterMuteForUid(boolean state, int flags, String callingPackage, IBinder cb,
-                int uid) {
-            setMasterMuteInternal(state, flags, callingPackage, cb, uid);
-        }
     }
 
     //==========================================================================================
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 46ab7e0..7084656 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -534,7 +534,8 @@
     public static final int SYNC_EVENT_NONE = 0;
     public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;
 
-    public static native int setDeviceConnectionState(int device, int state, String device_address);
+    public static native int setDeviceConnectionState(int device, int state,
+                                                      String device_address, String device_name);
     public static native int getDeviceConnectionState(int device, String device_address);
     public static native int setPhoneState(int state);
     public static native int setForceUse(int usage, int config);
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 1af0372..caccb6e 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1237,8 +1237,14 @@
 
     /**
      * Flushes the audio data currently queued for playback. Any data that has
-     * not been played back will be discarded.  No-op if not stopped or paused,
+     * been written but not yet presented will be discarded.  No-op if not stopped or paused,
      * or if the track's creation mode is not {@link #MODE_STREAM}.
+     * <BR> Note that although data written but not yet presented is discarded, there is no
+     * guarantee that all of the buffer space formerly used by that data
+     * is available for a subsequent write.
+     * For example, a call to {@link #write(byte[], int, int)} with <code>sizeInBytes</code>
+     * less than or equal to the total buffer size
+     * may return a short actual transfer count.
      */
     public void flush() {
         if (mState == STATE_INITIALIZED) {
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index ec33c5c..d34b21b 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -666,7 +666,7 @@
             if (pac.isIndentPAC()) {
                 moveCursorTo(pac.getRow(), pac.getCol());
             } else {
-                moveCursorToRow(pac.getRow());
+                moveCursorTo(pac.getRow(), 1);
             }
             getLineBuffer(mRow).setPACAt(mCol, pac);
         }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fad3cec..dabd9c2 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -52,16 +52,10 @@
 
     void setMasterVolume(int index, int flags, String callingPackage);
 
-    void setStreamSolo(int streamType, boolean state, IBinder cb);
-
-    void setStreamMute(int streamType, boolean state, IBinder cb);
-
     boolean isStreamMute(int streamType);
 
     void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
 
-    void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
-
     boolean isMasterMute();
 
     int getStreamVolume(int streamType);
@@ -195,7 +189,7 @@
     IRingtonePlayer getRingtonePlayer();
     int getMasterStreamType();
 
-    void setWiredDeviceConnectionState(int device, int state, String name);
+    void setWiredDeviceConnectionState(int type, int state, String address, String name);
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
 
     AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 0d6b91a..53ab264 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -214,6 +214,11 @@
          * the underlying data could be mapped as a pointer in JNI without doing
          * any copies with {@code GetDirectBufferAddress}.</p>
          *
+         * <p>For raw formats, each plane is only guaranteed to contain data
+         * up to the last pixel in the last row. In other words, the stride
+         * after the last row may not be mapped into the buffer. This is a
+         * necessary requirement for any interleaved format.</p>
+         *
          * @return the byte buffer containing the image data for this plane.
          */
         public abstract ByteBuffer getBuffer();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index e74399c..6ac854f 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -881,8 +881,8 @@
                         (int)(mAspectRatioRange.getUpper().doubleValue() * height));
                 return range;
             } catch (IllegalArgumentException e) {
-                // should not be here
-                Log.w(TAG, "could not get supported widths for " + height , e);
+                // height is not supported because there are no suitable widths
+                Log.v(TAG, "could not get supported widths for " + height);
                 throw new IllegalArgumentException("unsupported height");
             }
         }
@@ -925,8 +925,8 @@
                         (int)(width / mAspectRatioRange.getLower().doubleValue()));
                 return range;
             } catch (IllegalArgumentException e) {
-                // should not be here
-                Log.w(TAG, "could not get supported heights for " + width , e);
+                // width is not supported because there are no suitable heights
+                Log.v(TAG, "could not get supported heights for " + width);
                 throw new IllegalArgumentException("unsupported width");
             }
         }
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 4f74bdd..32d5b82 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -543,11 +543,6 @@
 
         public int load(String path, int priority)
         {
-            // pass network streams to player
-            if (path.startsWith("http:"))
-                return _load(path, priority);
-
-            // try local path
             int id = 0;
             try {
                 File f = new File(path);
@@ -562,6 +557,7 @@
             return id;
         }
 
+        @Override
         public int load(Context context, int resId, int priority) {
             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
             int id = 0;
@@ -576,6 +572,7 @@
             return id;
         }
 
+        @Override
         public int load(AssetFileDescriptor afd, int priority) {
             if (afd != null) {
                 long len = afd.getLength();
@@ -588,16 +585,17 @@
             }
         }
 
+        @Override
         public int load(FileDescriptor fd, long offset, long length, int priority) {
             return _load(fd, offset, length, priority);
         }
 
-        private native final int _load(String uri, int priority);
-
         private native final int _load(FileDescriptor fd, long offset, long length, int priority);
 
+        @Override
         public native final boolean unload(int soundID);
 
+        @Override
         public final int play(int soundID, float leftVolume, float rightVolume,
                 int priority, int loop, float rate) {
             if (isRestricted()) {
@@ -620,16 +618,22 @@
             }
         }
 
+        @Override
         public native final void pause(int streamID);
 
+        @Override
         public native final void resume(int streamID);
 
+        @Override
         public native final void autoPause();
 
+        @Override
         public native final void autoResume();
 
+        @Override
         public native final void stop(int streamID);
 
+        @Override
         public final void setVolume(int streamID, float leftVolume, float rightVolume) {
             if (isRestricted()) {
                 return;
@@ -639,16 +643,21 @@
 
         private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
 
+        @Override
         public void setVolume(int streamID, float volume) {
             setVolume(streamID, volume, volume);
         }
 
+        @Override
         public native final void setPriority(int streamID, int priority);
 
+        @Override
         public native final void setLoop(int streamID, int loop);
 
+        @Override
         public native final void setRate(int streamID, float rate);
 
+        @Override
         public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
         {
             synchronized(mLock) {
@@ -729,52 +738,69 @@
             return 0;
         }
 
+        @Override
         public int load(Context context, int resId, int priority) {
             return 0;
         }
 
+        @Override
         public int load(AssetFileDescriptor afd, int priority) {
             return 0;
         }
 
+        @Override
         public int load(FileDescriptor fd, long offset, long length, int priority) {
             return 0;
         }
 
+        @Override
         public final boolean unload(int soundID) {
             return true;
         }
 
+        @Override
         public final int play(int soundID, float leftVolume, float rightVolume,
                 int priority, int loop, float rate) {
             return 0;
         }
 
+        @Override
         public final void pause(int streamID) { }
 
+        @Override
         public final void resume(int streamID) { }
 
+        @Override
         public final void autoPause() { }
 
+        @Override
         public final void autoResume() { }
 
+        @Override
         public final void stop(int streamID) { }
 
+        @Override
         public final void setVolume(int streamID,
                 float leftVolume, float rightVolume) { }
 
+        @Override
         public void setVolume(int streamID, float volume) {
         }
 
+        @Override
         public final void setPriority(int streamID, int priority) { }
 
+        @Override
         public final void setLoop(int streamID, int loop) { }
 
+        @Override
         public final void setRate(int streamID, float rate) { }
 
+        @Override
         public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
         }
 
+        @Override
         public final void release() { }
     }
 }
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index e82567c..ef8d169 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -33,6 +33,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.service.media.MediaBrowserService;
 import android.service.media.IMediaBrowserService;
 import android.service.media.IMediaBrowserServiceCallbacks;
@@ -347,8 +348,8 @@
      */
     public void unsubscribe(@NonNull String parentId) {
         // Check arguments.
-        if (parentId == null) {
-            throw new IllegalArgumentException("parentId is null");
+        if (TextUtils.isEmpty(parentId)) {
+            throw new IllegalArgumentException("parentId is empty.");
         }
 
         // Remove from our list.
@@ -367,6 +368,60 @@
     }
 
     /**
+     * Retrieves a specific {@link MediaItem} from the connected service. Not
+     * all services may support this, so falling back to subscribing to the
+     * parent's id should be used when unavailable.
+     *
+     * @param mediaId The id of the item to retrieve.
+     * @param cb The callback to receive the result on.
+     */
+    public void getMediaItem(@NonNull String mediaId, @NonNull final MediaItemCallback cb) {
+        if (TextUtils.isEmpty(mediaId)) {
+            throw new IllegalArgumentException("mediaId is empty.");
+        }
+        if (cb == null) {
+            throw new IllegalArgumentException("cb is null.");
+        }
+        if (mState != CONNECT_STATE_CONNECTED) {
+            Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    cb.onError();
+                }
+            });
+            return;
+        }
+        ResultReceiver receiver = new ResultReceiver(mHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode != 0 || resultData == null
+                        || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) {
+                    cb.onError();
+                    return;
+                }
+                Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM);
+                if (!(item instanceof MediaItem)) {
+                    cb.onError();
+                }
+                cb.onMediaItemLoaded((MediaItem) resultData.getParcelable(
+                        MediaBrowserService.KEY_MEDIA_ITEM));
+            }
+        };
+        try {
+            mServiceBinder.getMediaItem(mediaId, receiver);
+        } catch (RemoteException e) {
+            Log.i(TAG, "Remote error getting media item.");
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    cb.onError();
+                }
+            });
+        }
+    }
+
+    /**
      * For debugging.
      */
     private static String getStateLabel(int state) {
@@ -690,6 +745,27 @@
     }
 
     /**
+     * Callback for receiving the result of {@link #getMediaItem}.
+     */
+    public static abstract class MediaItemCallback {
+
+        /**
+         * Called when the item has been returned by the browser service.
+         *
+         * @param item The item that was returned or null if it doesn't exist.
+         */
+        public void onMediaItemLoaded(MediaItem item) {
+        }
+
+        /**
+         * Called when the id doesn't exist or there was an error retrieving the
+         * item.
+         */
+        public void onError() {
+        }
+    }
+
+    /**
      * ServiceConnection to the other app.
      */
     private class MediaServiceConnection implements ServiceConnection {
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 7ea269b..4ea22f9 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -221,14 +221,10 @@
                 mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
                         direction, flags);
             } else if (isMute) {
-                if (down) {
-                    // We need to send two volume events on down, one to mute
-                    // and one to show the UI
+                if (down && keyEvent.getRepeatCount() == 0) {
                     mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                            MediaSessionManager.DIRECTION_MUTE, flags);
+                            AudioManager.ADJUST_TOGGLE_MUTE, flags);
                 }
-                mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                        0 /* direction, causes UI to show on down */, flags);
             }
         }
     }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index a4ef851..b4fff8f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -59,14 +59,6 @@
     private Context mContext;
 
     /**
-     * Special flag for sending the mute key to dispatchAdjustVolume used by the
-     * system.
-     *
-     * @hide
-     */
-    public static final int DIRECTION_MUTE = -99;
-
-    /**
      * @hide
      */
     public MediaSessionManager(Context context) {
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 5b92266..bc9722e 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -756,8 +756,9 @@
          * </p><p>
          * Note that this sub-directory also supports opening the logo as an asset file in write
          * mode.  Callers can create or replace the primary logo associated with this channel by
-         * opening the asset file and writing the full-size photo contents into it.  When the file
-         * is closed, the image will be parsed, sized down if necessary, and stored.
+         * opening the asset file and writing the full-size photo contents into it. (Make sure there
+         * is no padding around the logo image.) When the file is closed, the image will be parsed,
+         * sized down if necessary, and stored.
          * </p><p>
          * Usage example:
          * <pre>
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 8c478ed..f29be0d 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -310,7 +310,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")");
-                        mSessionCallback.onSessionEvent(eventType, eventArgs);
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onSessionEvent(eventType, eventArgs);
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in sending event (event=" + eventType + ")");
                     }
@@ -329,7 +331,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyChannelRetuned");
-                        mSessionCallback.onChannelRetuned(channelUri);
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onChannelRetuned(channelUri);
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyChannelRetuned");
                     }
@@ -366,7 +370,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyTracksChanged");
-                        mSessionCallback.onTracksChanged(tracks);
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onTracksChanged(tracks);
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyTracksChanged");
                     }
@@ -394,7 +400,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyTrackSelected");
-                        mSessionCallback.onTrackSelected(type, trackId);
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onTrackSelected(type, trackId);
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyTrackSelected");
                     }
@@ -415,7 +423,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyVideoAvailable");
-                        mSessionCallback.onVideoAvailable();
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onVideoAvailable();
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyVideoAvailable");
                     }
@@ -447,7 +457,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyVideoUnavailable");
-                        mSessionCallback.onVideoUnavailable(reason);
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onVideoUnavailable(reason);
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyVideoUnavailable");
                     }
@@ -486,7 +498,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyContentAllowed");
-                        mSessionCallback.onContentAllowed();
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onContentAllowed();
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyContentAllowed");
                     }
@@ -526,7 +540,9 @@
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyContentBlocked");
-                        mSessionCallback.onContentBlocked(rating.flattenToString());
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onContentBlocked(rating.flattenToString());
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyContentBlocked");
                     }
@@ -557,7 +573,9 @@
                     try {
                         if (DEBUG) Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top + ", r="
                                 + right + ", b=" + bottom + ",)");
-                        mSessionCallback.onLayoutSurface(left, top, right, bottom);
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onLayoutSurface(left, top, right, bottom);
+                        }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in layoutSurface");
                     }
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index 01285ee..f01fc07 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -6,6 +6,7 @@
 import android.service.media.IMediaBrowserServiceCallbacks;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.ResultReceiver;
 
 /**
  * Media API allows clients to browse through hierarchy of a user’s media collection,
@@ -18,4 +19,5 @@
 
     void addSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
+    void getMediaItem(String uri, in ResultReceiver cb);
 }
\ No newline at end of file
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 47cb94b..8287344 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -32,8 +32,10 @@
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.service.media.IMediaBrowserService;
 import android.service.media.IMediaBrowserServiceCallbacks;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -74,6 +76,13 @@
     @SdkConstant(SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
 
+    /**
+     * A key for passing the MediaItem to the ResultReceiver in getMediaItem.
+     *
+     * @hide
+     */
+    public static final String KEY_MEDIA_ITEM = "media_item";
+
     private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
     private final Handler mHandler = new Handler();
     private ServiceBinder mBinder;
@@ -261,6 +270,33 @@
                 }
             });
         }
+
+        @Override
+        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
+            if (TextUtils.isEmpty(mediaId) || receiver == null) {
+                return;
+            }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final Result<MediaBrowser.MediaItem> result
+                            = new Result<MediaBrowser.MediaItem>(mediaId) {
+                        @Override
+                        void onResultSent(MediaBrowser.MediaItem item) {
+                            Bundle bundle = new Bundle();
+                            bundle.putParcelable(KEY_MEDIA_ITEM, item);
+                            receiver.send(0, bundle);
+                        }
+                    };
+                    try {
+                        MediaBrowserService.this.getMediaItem(mediaId, result);
+                    } catch (UnsupportedOperationException e) {
+                        receiver.send(-1, null);
+                    }
+                }
+            });
+        }
     }
 
     @Override
@@ -284,20 +320,21 @@
     /**
      * Called to get the root information for browsing by a particular client.
      * <p>
-     * The implementation should verify that the client package has
-     * permission to access browse media information before returning
-     * the root id; it should return null if the client is not
-     * allowed to access this information.
+     * The implementation should verify that the client package has permission
+     * to access browse media information before returning the root id; it
+     * should return null if the client is not allowed to access this
+     * information.
      * </p>
      *
-     * @param clientPackageName The package name of the application
-     * which is requesting access to browse media.
-     * @param clientUid The uid of the application which is requesting
-     * access to browse media.
+     * @param clientPackageName The package name of the application which is
+     *            requesting access to browse media.
+     * @param clientUid The uid of the application which is requesting access to
+     *            browse media.
      * @param rootHints An optional bundle of service-specific arguments to send
-     * to the media browse service when connecting and retrieving the root id
-     * for browsing, or null if none.  The contents of this bundle may affect
-     * the information returned when browsing.
+     *            to the media browse service when connecting and retrieving the
+     *            root id for browsing, or null if none. The contents of this
+     *            bundle may affect the information returned when browsing.
+     * @return The {@link BrowserRoot} for accessing this app's content or null.
      */
     public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
             int clientUid, @Nullable Bundle rootHints);
@@ -305,24 +342,51 @@
     /**
      * Called to get information about the children of a media item.
      * <p>
-     * Implementations must call result.{@link Result#sendResult result.sendResult} with the list
-     * of children. If loading the children will be an expensive operation that should be performed
-     * on another thread, result.{@link Result#detach result.detach} may be called before returning
-     * from this function, and then {@link Result#sendResult result.sendResult} called when
-     * the loading is complete.
+     * Implementations must call {@link Result#sendResult result.sendResult}
+     * with the list of children. If loading the children will be an expensive
+     * operation that should be performed on another thread,
+     * {@link Result#detach result.detach} may be called before returning from
+     * this function, and then {@link Result#sendResult result.sendResult}
+     * called when the loading is complete.
      *
-     * @param parentId The id of the parent media item whose
-     * children are to be queried.
-     * @return The list of children, or null if the id is invalid.
+     * @param parentId The id of the parent media item whose children are to be
+     *            queried.
+     * @param result The Result to send the list of children to, or null if the
+     *            id is invalid.
      */
     public abstract void onLoadChildren(@NonNull String parentId,
             @NonNull Result<List<MediaBrowser.MediaItem>> result);
 
     /**
+     * Called to get a specific media item. The mediaId should be the same id
+     * that would be returned for this item when it is in a list of child items.
+     * <p>
+     * Implementations must call {@link Result#sendResult result.sendResult}. If
+     * loading the item will be an expensive operation {@link Result#detach
+     * result.detach} may be called before returning from this function, and
+     * then {@link Result#sendResult result.sendResult} called when the item has
+     * been loaded.
+     * <p>
+     * The default implementation throws an exception.
+     *
+     * @param mediaId The id for the specific
+     *            {@link android.media.browse.MediaBrowser.MediaItem}.
+     * @param result The Result to send the item to, or null if the id is
+     *            invalid.
+     * @throws UnsupportedOperationException
+     */
+    public void getMediaItem(String mediaId, Result<MediaBrowser.MediaItem> result)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException("getMediaItem is not supported.");
+    }
+
+    /**
      * Call to set the media session.
      * <p>
      * This should be called as soon as possible during the service's startup.
      * It may only be called once.
+     *
+     * @param token The token for the service's {@link MediaSession}.
      */
     public void setSessionToken(final MediaSession.Token token) {
         if (token == null) {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 7e68c78..5406130 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -351,7 +351,7 @@
     int bytesPerPixel = 0;
 
     dataSize = ySize = cSize = cStride = 0;
-    int32_t fmt = buffer->format;
+    int32_t fmt = buffer->flexFormat;
 
     bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, readerFormat);
     fmt = applyFormatOverrides(fmt, readerFormat);
@@ -363,18 +363,21 @@
                 (idx == 1) ?
                     buffer->dataCb :
                 buffer->dataCr;
+            // only map until last pixel
             if (idx == 0) {
-                dataSize = buffer->stride * buffer->height;
+                dataSize = buffer->stride * (buffer->height - 1) + buffer->width;
             } else {
-                dataSize = buffer->chromaStride * buffer->height / 2;
+                dataSize = buffer->chromaStride * (buffer->height / 2 - 1) +
+                        buffer->chromaStep * (buffer->width / 2 - 1) + 1;
             }
             break;
         // NV21
         case HAL_PIXEL_FORMAT_YCrCb_420_SP:
             cr = buffer->data + (buffer->stride * buffer->height);
             cb = cr + 1;
-            ySize = buffer->width * buffer->height;
-            cSize = buffer->width * buffer->height / 2;
+            // only map until last pixel
+            ySize = buffer->width * (buffer->height - 1) + buffer->width;
+            cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1;
 
             pData =
                 (idx == 0) ?
@@ -488,7 +491,7 @@
     int pixelStride = 0;
     ALOG_ASSERT(buffer != NULL, "buffer is NULL");
 
-    int32_t fmt = buffer->format;
+    int32_t fmt = buffer->flexFormat;
 
     fmt = applyFormatOverrides(fmt, readerFormat);
 
@@ -548,7 +551,7 @@
     int rowStride = 0;
     ALOG_ASSERT(buffer != NULL, "buffer is NULL");
 
-    int32_t fmt = buffer->format;
+    int32_t fmt = buffer->flexFormat;
 
     fmt = applyFormatOverrides(fmt, readerFormat);
 
@@ -796,7 +799,7 @@
         return ACQUIRE_NO_BUFFERS;
     }
 
-    if (buffer->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+    if (buffer->flexFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
         jniThrowException(env, "java/lang/UnsupportedOperationException",
                 "NV21 format is not supported by ImageReader");
         return -1;
@@ -825,8 +828,10 @@
     }
 
     int bufFmt = buffer->format;
+    if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+        bufFmt = buffer->flexFormat;
+    }
     if (imgReaderFmt != bufFmt) {
-
         if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
                 HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
             // Special casing for when producer switches to a format compatible with flexible YUV
@@ -848,7 +853,7 @@
             String8 msg;
             msg.appendFormat("The producer output buffer format 0x%x doesn't "
                     "match the ImageReader's configured buffer format 0x%x.",
-                    buffer->format, ctx->getBufferFormat());
+                    bufFmt, ctx->getBufferFormat());
             jniThrowException(env, "java/lang/UnsupportedOperationException",
                     msg.string());
             return -1;
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index ea33a2f..e101709 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -232,28 +232,28 @@
                         env,
                         hashMap,
                         hashMapPutID,
-                        StringPrintf("%s-left", key).c_str(),
+                        AStringPrintf("%s-left", key).c_str(),
                         left);
 
                 SetMapInt32(
                         env,
                         hashMap,
                         hashMapPutID,
-                        StringPrintf("%s-top", key).c_str(),
+                        AStringPrintf("%s-top", key).c_str(),
                         top);
 
                 SetMapInt32(
                         env,
                         hashMap,
                         hashMapPutID,
-                        StringPrintf("%s-right", key).c_str(),
+                        AStringPrintf("%s-right", key).c_str(),
                         right);
 
                 SetMapInt32(
                         env,
                         hashMap,
                         hashMapPutID,
-                        StringPrintf("%s-bottom", key).c_str(),
+                        AStringPrintf("%s-bottom", key).c_str(),
                         bottom);
                 break;
             }
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 3382512..71ab013 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    android_media_SoundPool_SoundPoolImpl.cpp
+    android_media_SoundPool_SoundPoolImpl.cpp \
+    SoundPool.cpp \
+    SoundPoolThread.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     liblog \
@@ -10,7 +12,9 @@
     libutils \
     libandroid_runtime \
     libnativehelper \
-    libmedia
+    libmedia \
+    libmediandk \
+    libbinder
 
 LOCAL_MODULE:= libsoundpool
 
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
new file mode 100644
index 0000000..a8b91d2
--- /dev/null
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -0,0 +1,1018 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool"
+
+#include <inttypes.h>
+
+#include <utils/Log.h>
+
+#define USE_SHARED_MEM_BUFFER
+
+#include <media/AudioTrack.h>
+#include <media/IMediaHTTPService.h>
+#include <media/mediaplayer.h>
+#include <media/stagefright/MediaExtractor.h>
+#include "SoundPool.h"
+#include "SoundPoolThread.h"
+#include <media/AudioPolicyHelper.h>
+#include <ndk/NdkMediaCodec.h>
+#include <ndk/NdkMediaExtractor.h>
+#include <ndk/NdkMediaFormat.h>
+
+namespace android
+{
+
+int kDefaultBufferCount = 4;
+uint32_t kMaxSampleRate = 48000;
+uint32_t kDefaultSampleRate = 44100;
+uint32_t kDefaultFrameCount = 1200;
+size_t kDefaultHeapSize = 1024 * 1024; // 1MB
+
+
+SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes)
+{
+    ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s",
+            maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags);
+
+    // check limits
+    mMaxChannels = maxChannels;
+    if (mMaxChannels < 1) {
+        mMaxChannels = 1;
+    }
+    else if (mMaxChannels > 32) {
+        mMaxChannels = 32;
+    }
+    ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
+
+    mQuit = false;
+    mDecodeThread = 0;
+    memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
+    mAllocated = 0;
+    mNextSampleID = 0;
+    mNextChannelID = 0;
+
+    mCallback = 0;
+    mUserData = 0;
+
+    mChannelPool = new SoundChannel[mMaxChannels];
+    for (int i = 0; i < mMaxChannels; ++i) {
+        mChannelPool[i].init(this);
+        mChannels.push_back(&mChannelPool[i]);
+    }
+
+    // start decode thread
+    startThreads();
+}
+
+SoundPool::~SoundPool()
+{
+    ALOGV("SoundPool destructor");
+    mDecodeThread->quit();
+    quit();
+
+    Mutex::Autolock lock(&mLock);
+
+    mChannels.clear();
+    if (mChannelPool)
+        delete [] mChannelPool;
+    // clean up samples
+    ALOGV("clear samples");
+    mSamples.clear();
+
+    if (mDecodeThread)
+        delete mDecodeThread;
+}
+
+void SoundPool::addToRestartList(SoundChannel* channel)
+{
+    Mutex::Autolock lock(&mRestartLock);
+    if (!mQuit) {
+        mRestart.push_back(channel);
+        mCondition.signal();
+    }
+}
+
+void SoundPool::addToStopList(SoundChannel* channel)
+{
+    Mutex::Autolock lock(&mRestartLock);
+    if (!mQuit) {
+        mStop.push_back(channel);
+        mCondition.signal();
+    }
+}
+
+int SoundPool::beginThread(void* arg)
+{
+    SoundPool* p = (SoundPool*)arg;
+    return p->run();
+}
+
+int SoundPool::run()
+{
+    mRestartLock.lock();
+    while (!mQuit) {
+        mCondition.wait(mRestartLock);
+        ALOGV("awake");
+        if (mQuit) break;
+
+        while (!mStop.empty()) {
+            SoundChannel* channel;
+            ALOGV("Getting channel from stop list");
+            List<SoundChannel* >::iterator iter = mStop.begin();
+            channel = *iter;
+            mStop.erase(iter);
+            mRestartLock.unlock();
+            if (channel != 0) {
+                Mutex::Autolock lock(&mLock);
+                channel->stop();
+            }
+            mRestartLock.lock();
+            if (mQuit) break;
+        }
+
+        while (!mRestart.empty()) {
+            SoundChannel* channel;
+            ALOGV("Getting channel from list");
+            List<SoundChannel*>::iterator iter = mRestart.begin();
+            channel = *iter;
+            mRestart.erase(iter);
+            mRestartLock.unlock();
+            if (channel != 0) {
+                Mutex::Autolock lock(&mLock);
+                channel->nextEvent();
+            }
+            mRestartLock.lock();
+            if (mQuit) break;
+        }
+    }
+
+    mStop.clear();
+    mRestart.clear();
+    mCondition.signal();
+    mRestartLock.unlock();
+    ALOGV("goodbye");
+    return 0;
+}
+
+void SoundPool::quit()
+{
+    mRestartLock.lock();
+    mQuit = true;
+    mCondition.signal();
+    mCondition.wait(mRestartLock);
+    ALOGV("return from quit");
+    mRestartLock.unlock();
+}
+
+bool SoundPool::startThreads()
+{
+    createThreadEtc(beginThread, this, "SoundPool");
+    if (mDecodeThread == NULL)
+        mDecodeThread = new SoundPoolThread(this);
+    return mDecodeThread != NULL;
+}
+
+SoundChannel* SoundPool::findChannel(int channelID)
+{
+    for (int i = 0; i < mMaxChannels; ++i) {
+        if (mChannelPool[i].channelID() == channelID) {
+            return &mChannelPool[i];
+        }
+    }
+    return NULL;
+}
+
+SoundChannel* SoundPool::findNextChannel(int channelID)
+{
+    for (int i = 0; i < mMaxChannels; ++i) {
+        if (mChannelPool[i].nextChannelID() == channelID) {
+            return &mChannelPool[i];
+        }
+    }
+    return NULL;
+}
+
+int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
+{
+    ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
+            fd, offset, length, priority);
+    Mutex::Autolock lock(&mLock);
+    sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length);
+    mSamples.add(sample->sampleID(), sample);
+    doLoad(sample);
+    return sample->sampleID();
+}
+
+void SoundPool::doLoad(sp<Sample>& sample)
+{
+    ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID());
+    sample->startLoad();
+    mDecodeThread->loadSample(sample->sampleID());
+}
+
+bool SoundPool::unload(int sampleID)
+{
+    ALOGV("unload: sampleID=%d", sampleID);
+    Mutex::Autolock lock(&mLock);
+    return mSamples.removeItem(sampleID);
+}
+
+int SoundPool::play(int sampleID, float leftVolume, float rightVolume,
+        int priority, int loop, float rate)
+{
+    ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
+            sampleID, leftVolume, rightVolume, priority, loop, rate);
+    sp<Sample> sample;
+    SoundChannel* channel;
+    int channelID;
+
+    Mutex::Autolock lock(&mLock);
+
+    if (mQuit) {
+        return 0;
+    }
+    // is sample ready?
+    sample = findSample(sampleID);
+    if ((sample == 0) || (sample->state() != Sample::READY)) {
+        ALOGW("  sample %d not READY", sampleID);
+        return 0;
+    }
+
+    dump();
+
+    // allocate a channel
+    channel = allocateChannel_l(priority);
+
+    // no channel allocated - return 0
+    if (!channel) {
+        ALOGV("No channel allocated");
+        return 0;
+    }
+
+    channelID = ++mNextChannelID;
+
+    ALOGV("play channel %p state = %d", channel, channel->state());
+    channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate);
+    return channelID;
+}
+
+SoundChannel* SoundPool::allocateChannel_l(int priority)
+{
+    List<SoundChannel*>::iterator iter;
+    SoundChannel* channel = NULL;
+
+    // allocate a channel
+    if (!mChannels.empty()) {
+        iter = mChannels.begin();
+        if (priority >= (*iter)->priority()) {
+            channel = *iter;
+            mChannels.erase(iter);
+            ALOGV("Allocated active channel");
+        }
+    }
+
+    // update priority and put it back in the list
+    if (channel) {
+        channel->setPriority(priority);
+        for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
+            if (priority < (*iter)->priority()) {
+                break;
+            }
+        }
+        mChannels.insert(iter, channel);
+    }
+    return channel;
+}
+
+// move a channel from its current position to the front of the list
+void SoundPool::moveToFront_l(SoundChannel* channel)
+{
+    for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
+        if (*iter == channel) {
+            mChannels.erase(iter);
+            mChannels.push_front(channel);
+            break;
+        }
+    }
+}
+
+void SoundPool::pause(int channelID)
+{
+    ALOGV("pause(%d)", channelID);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->pause();
+    }
+}
+
+void SoundPool::autoPause()
+{
+    ALOGV("autoPause()");
+    Mutex::Autolock lock(&mLock);
+    for (int i = 0; i < mMaxChannels; ++i) {
+        SoundChannel* channel = &mChannelPool[i];
+        channel->autoPause();
+    }
+}
+
+void SoundPool::resume(int channelID)
+{
+    ALOGV("resume(%d)", channelID);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->resume();
+    }
+}
+
+void SoundPool::autoResume()
+{
+    ALOGV("autoResume()");
+    Mutex::Autolock lock(&mLock);
+    for (int i = 0; i < mMaxChannels; ++i) {
+        SoundChannel* channel = &mChannelPool[i];
+        channel->autoResume();
+    }
+}
+
+void SoundPool::stop(int channelID)
+{
+    ALOGV("stop(%d)", channelID);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->stop();
+    } else {
+        channel = findNextChannel(channelID);
+        if (channel)
+            channel->clearNextEvent();
+    }
+}
+
+void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume)
+{
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setVolume(leftVolume, rightVolume);
+    }
+}
+
+void SoundPool::setPriority(int channelID, int priority)
+{
+    ALOGV("setPriority(%d, %d)", channelID, priority);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setPriority(priority);
+    }
+}
+
+void SoundPool::setLoop(int channelID, int loop)
+{
+    ALOGV("setLoop(%d, %d)", channelID, loop);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setLoop(loop);
+    }
+}
+
+void SoundPool::setRate(int channelID, float rate)
+{
+    ALOGV("setRate(%d, %f)", channelID, rate);
+    Mutex::Autolock lock(&mLock);
+    SoundChannel* channel = findChannel(channelID);
+    if (channel) {
+        channel->setRate(rate);
+    }
+}
+
+// call with lock held
+void SoundPool::done_l(SoundChannel* channel)
+{
+    ALOGV("done_l(%d)", channel->channelID());
+    // if "stolen", play next event
+    if (channel->nextChannelID() != 0) {
+        ALOGV("add to restart list");
+        addToRestartList(channel);
+    }
+
+    // return to idle state
+    else {
+        ALOGV("move to front");
+        moveToFront_l(channel);
+    }
+}
+
+void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
+{
+    Mutex::Autolock lock(&mCallbackLock);
+    mCallback = callback;
+    mUserData = user;
+}
+
+void SoundPool::notify(SoundPoolEvent event)
+{
+    Mutex::Autolock lock(&mCallbackLock);
+    if (mCallback != NULL) {
+        mCallback(event, this, mUserData);
+    }
+}
+
+void SoundPool::dump()
+{
+    for (int i = 0; i < mMaxChannels; ++i) {
+        mChannelPool[i].dump();
+    }
+}
+
+
+Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length)
+{
+    init();
+    mSampleID = sampleID;
+    mFd = dup(fd);
+    mOffset = offset;
+    mLength = length;
+    ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64,
+        mSampleID, mFd, mLength, mOffset);
+}
+
+void Sample::init()
+{
+    mSize = 0;
+    mRefCount = 0;
+    mSampleID = 0;
+    mState = UNLOADED;
+    mFd = -1;
+    mOffset = 0;
+    mLength = 0;
+}
+
+Sample::~Sample()
+{
+    ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd);
+    if (mFd > 0) {
+        ALOGV("close(%d)", mFd);
+        ::close(mFd);
+    }
+}
+
+static status_t decode(int fd, int64_t offset, int64_t length,
+        uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
+        sp<MemoryHeapBase> heap, size_t *memsize) {
+
+    ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
+    AMediaExtractor *ex = AMediaExtractor_new();
+    status_t err = AMediaExtractor_setDataSourceFd(ex, fd, offset, length);
+
+    if (err != AMEDIA_OK) {
+        return err;
+    }
+
+    *audioFormat = AUDIO_FORMAT_PCM_16_BIT;
+
+    size_t numTracks = AMediaExtractor_getTrackCount(ex);
+    for (size_t i = 0; i < numTracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            AMediaExtractor_delete(ex);
+            AMediaFormat_delete(format);
+            return UNKNOWN_ERROR;
+        }
+        if (strncmp(mime, "audio/", 6) == 0) {
+
+            AMediaCodec *codec = AMediaCodec_createDecoderByType(mime);
+            if (AMediaCodec_configure(codec, format,
+                    NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
+                || AMediaCodec_start(codec) != AMEDIA_OK
+                || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
+                AMediaExtractor_delete(ex);
+                AMediaCodec_delete(codec);
+                AMediaFormat_delete(format);
+                return UNKNOWN_ERROR;
+            }
+
+            bool sawInputEOS = false;
+            bool sawOutputEOS = false;
+            uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
+            size_t available = heap->getSize();
+            size_t written = 0;
+
+            AMediaFormat_delete(format);
+            format = AMediaCodec_getOutputFormat(codec);
+
+            while (!sawOutputEOS) {
+                if (!sawInputEOS) {
+                    ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
+                    ALOGV("input buffer %zd", bufidx);
+                    if (bufidx >= 0) {
+                        size_t bufsize;
+                        uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
+                        int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+                        ALOGV("read %d", sampleSize);
+                        if (sampleSize < 0) {
+                            sampleSize = 0;
+                            sawInputEOS = true;
+                            ALOGV("EOS");
+                        }
+                        int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+                        AMediaCodec_queueInputBuffer(codec, bufidx,
+                                0 /* offset */, sampleSize, presentationTimeUs,
+                                sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+                        AMediaExtractor_advance(ex);
+                    }
+                }
+
+                AMediaCodecBufferInfo info;
+                int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
+                ALOGV("dequeueoutput returned: %d", status);
+                if (status >= 0) {
+                    if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+                        ALOGV("output EOS");
+                        sawOutputEOS = true;
+                    }
+                    ALOGV("got decoded buffer size %d", info.size);
+
+                    uint8_t *buf = AMediaCodec_getOutputBuffer(codec, status, NULL /* out_size */);
+                    size_t dataSize = info.size;
+                    if (dataSize > available) {
+                        dataSize = available;
+                    }
+                    memcpy(writePos, buf + info.offset, dataSize);
+                    writePos += dataSize;
+                    written += dataSize;
+                    available -= dataSize;
+                    AMediaCodec_releaseOutputBuffer(codec, status, false /* render */);
+                    if (available == 0) {
+                        // there might be more data, but there's no space for it
+                        sawOutputEOS = true;
+                    }
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+                    ALOGV("output buffers changed");
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+                    AMediaFormat_delete(format);
+                    format = AMediaCodec_getOutputFormat(codec);
+                    ALOGV("format changed to: %s", AMediaFormat_toString(format));
+                } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+                    ALOGV("no output buffer right now");
+                } else {
+                    ALOGV("unexpected info code: %d", status);
+                }
+            }
+
+            AMediaCodec_stop(codec);
+            AMediaCodec_delete(codec);
+            AMediaExtractor_delete(ex);
+            if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
+                    !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels)) {
+                AMediaFormat_delete(format);
+                return UNKNOWN_ERROR;
+            }
+            AMediaFormat_delete(format);
+            *memsize = written;
+            return OK;
+        }
+        AMediaFormat_delete(format);
+    }
+    AMediaExtractor_delete(ex);
+    return UNKNOWN_ERROR;
+}
+
+status_t Sample::doLoad()
+{
+    uint32_t sampleRate;
+    int numChannels;
+    audio_format_t format;
+    status_t status;
+    mHeap = new MemoryHeapBase(kDefaultHeapSize);
+
+    ALOGV("Start decode");
+    status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
+                                 mHeap, &mSize);
+    ALOGV("close(%d)", mFd);
+    ::close(mFd);
+    mFd = -1;
+    if (status != NO_ERROR) {
+        ALOGE("Unable to load sample");
+        goto error;
+    }
+    ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d",
+          mHeap->getBase(), mSize, sampleRate, numChannels);
+
+    if (sampleRate > kMaxSampleRate) {
+       ALOGE("Sample rate (%u) out of range", sampleRate);
+       status = BAD_VALUE;
+       goto error;
+    }
+
+    if ((numChannels < 1) || (numChannels > 2)) {
+        ALOGE("Sample channel count (%d) out of range", numChannels);
+        status = BAD_VALUE;
+        goto error;
+    }
+
+    mData = new MemoryBase(mHeap, 0, mSize);
+    mSampleRate = sampleRate;
+    mNumChannels = numChannels;
+    mFormat = format;
+    mState = READY;
+    return NO_ERROR;
+
+error:
+    mHeap.clear();
+    return status;
+}
+
+
+void SoundChannel::init(SoundPool* soundPool)
+{
+    mSoundPool = soundPool;
+}
+
+// call with sound pool lock held
+void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume,
+        float rightVolume, int priority, int loop, float rate)
+{
+    sp<AudioTrack> oldTrack;
+    sp<AudioTrack> newTrack;
+    status_t status;
+
+    { // scope for the lock
+        Mutex::Autolock lock(&mLock);
+
+        ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f,"
+                " priority=%d, loop=%d, rate=%f",
+                this, sample->sampleID(), nextChannelID, leftVolume, rightVolume,
+                priority, loop, rate);
+
+        // if not idle, this voice is being stolen
+        if (mState != IDLE) {
+            ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
+            mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
+            stop_l();
+            return;
+        }
+
+        // initialize track
+        size_t afFrameCount;
+        uint32_t afSampleRate;
+        audio_stream_type_t streamType = audio_attributes_to_stream_type(mSoundPool->attributes());
+        if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+            afFrameCount = kDefaultFrameCount;
+        }
+        if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+            afSampleRate = kDefaultSampleRate;
+        }
+        int numChannels = sample->numChannels();
+        uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
+        size_t frameCount = 0;
+
+        if (loop) {
+            frameCount = sample->size()/numChannels/
+                ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
+        }
+
+#ifndef USE_SHARED_MEM_BUFFER
+        uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
+        // Ensure minimum audio buffer size in case of short looped sample
+        if(frameCount < totalFrames) {
+            frameCount = totalFrames;
+        }
+#endif
+
+        // mToggle toggles each time a track is started on a given channel.
+        // The toggle is concatenated with the SoundChannel address and passed to AudioTrack
+        // as callback user data. This enables the detection of callbacks received from the old
+        // audio track while the new one is being started and avoids processing them with
+        // wrong audio audio buffer size  (mAudioBufferSize)
+        unsigned long toggle = mToggle ^ 1;
+        void *userData = (void *)((unsigned long)this | toggle);
+        audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels);
+
+        // do not create a new audio track if current track is compatible with sample parameters
+#ifdef USE_SHARED_MEM_BUFFER
+        newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
+                channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData);
+#else
+        uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
+        newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
+                channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
+                bufferFrames);
+#endif
+        oldTrack = mAudioTrack;
+        status = newTrack->initCheck();
+        if (status != NO_ERROR) {
+            ALOGE("Error creating AudioTrack");
+            goto exit;
+        }
+        ALOGV("setVolume %p", newTrack.get());
+        newTrack->setVolume(leftVolume, rightVolume);
+        newTrack->setLoop(0, frameCount, loop);
+
+        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+        mToggle = toggle;
+        mAudioTrack = newTrack;
+        mPos = 0;
+        mSample = sample;
+        mChannelID = nextChannelID;
+        mPriority = priority;
+        mLoop = loop;
+        mLeftVolume = leftVolume;
+        mRightVolume = rightVolume;
+        mNumChannels = numChannels;
+        mRate = rate;
+        clearNextEvent();
+        mState = PLAYING;
+        mAudioTrack->start();
+        mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize();
+    }
+
+exit:
+    ALOGV("delete oldTrack %p", oldTrack.get());
+    if (status != NO_ERROR) {
+        mAudioTrack.clear();
+    }
+}
+
+void SoundChannel::nextEvent()
+{
+    sp<Sample> sample;
+    int nextChannelID;
+    float leftVolume;
+    float rightVolume;
+    int priority;
+    int loop;
+    float rate;
+
+    // check for valid event
+    {
+        Mutex::Autolock lock(&mLock);
+        nextChannelID = mNextEvent.channelID();
+        if (nextChannelID  == 0) {
+            ALOGV("stolen channel has no event");
+            return;
+        }
+
+        sample = mNextEvent.sample();
+        leftVolume = mNextEvent.leftVolume();
+        rightVolume = mNextEvent.rightVolume();
+        priority = mNextEvent.priority();
+        loop = mNextEvent.loop();
+        rate = mNextEvent.rate();
+    }
+
+    ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID);
+    play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
+}
+
+void SoundChannel::callback(int event, void* user, void *info)
+{
+    SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
+
+    channel->process(event, info, (unsigned long)user & 1);
+}
+
+void SoundChannel::process(int event, void *info, unsigned long toggle)
+{
+    //ALOGV("process(%d)", mChannelID);
+
+    Mutex::Autolock lock(&mLock);
+
+    AudioTrack::Buffer* b = NULL;
+    if (event == AudioTrack::EVENT_MORE_DATA) {
+       b = static_cast<AudioTrack::Buffer *>(info);
+    }
+
+    if (mToggle != toggle) {
+        ALOGV("process wrong toggle %p channel %d", this, mChannelID);
+        if (b != NULL) {
+            b->size = 0;
+        }
+        return;
+    }
+
+    sp<Sample> sample = mSample;
+
+//    ALOGV("SoundChannel::process event %d", event);
+
+    if (event == AudioTrack::EVENT_MORE_DATA) {
+
+        // check for stop state
+        if (b->size == 0) return;
+
+        if (mState == IDLE) {
+            b->size = 0;
+            return;
+        }
+
+        if (sample != 0) {
+            // fill buffer
+            uint8_t* q = (uint8_t*) b->i8;
+            size_t count = 0;
+
+            if (mPos < (int)sample->size()) {
+                uint8_t* p = sample->data() + mPos;
+                count = sample->size() - mPos;
+                if (count > b->size) {
+                    count = b->size;
+                }
+                memcpy(q, p, count);
+//              ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size,
+//                      count);
+            } else if (mPos < mAudioBufferSize) {
+                count = mAudioBufferSize - mPos;
+                if (count > b->size) {
+                    count = b->size;
+                }
+                memset(q, 0, count);
+//              ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count);
+            }
+
+            mPos += count;
+            b->size = count;
+            //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
+        }
+    } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) {
+        ALOGV("process %p channel %d event %s",
+              this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
+                      "BUFFER_END");
+        mSoundPool->addToStopList(this);
+    } else if (event == AudioTrack::EVENT_LOOP_END) {
+        ALOGV("End loop %p channel %d", this, mChannelID);
+    } else if (event == AudioTrack::EVENT_NEW_IAUDIOTRACK) {
+        ALOGV("process %p channel %d NEW_IAUDIOTRACK", this, mChannelID);
+    } else {
+        ALOGW("SoundChannel::process unexpected event %d", event);
+    }
+}
+
+
+// call with lock held
+bool SoundChannel::doStop_l()
+{
+    if (mState != IDLE) {
+        setVolume_l(0, 0);
+        ALOGV("stop");
+        mAudioTrack->stop();
+        mSample.clear();
+        mState = IDLE;
+        mPriority = IDLE_PRIORITY;
+        return true;
+    }
+    return false;
+}
+
+// call with lock held and sound pool lock held
+void SoundChannel::stop_l()
+{
+    if (doStop_l()) {
+        mSoundPool->done_l(this);
+    }
+}
+
+// call with sound pool lock held
+void SoundChannel::stop()
+{
+    bool stopped;
+    {
+        Mutex::Autolock lock(&mLock);
+        stopped = doStop_l();
+    }
+
+    if (stopped) {
+        mSoundPool->done_l(this);
+    }
+}
+
+//FIXME: Pause is a little broken right now
+void SoundChannel::pause()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mState == PLAYING) {
+        ALOGV("pause track");
+        mState = PAUSED;
+        mAudioTrack->pause();
+    }
+}
+
+void SoundChannel::autoPause()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mState == PLAYING) {
+        ALOGV("pause track");
+        mState = PAUSED;
+        mAutoPaused = true;
+        mAudioTrack->pause();
+    }
+}
+
+void SoundChannel::resume()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mState == PAUSED) {
+        ALOGV("resume track");
+        mState = PLAYING;
+        mAutoPaused = false;
+        mAudioTrack->start();
+    }
+}
+
+void SoundChannel::autoResume()
+{
+    Mutex::Autolock lock(&mLock);
+    if (mAutoPaused && (mState == PAUSED)) {
+        ALOGV("resume track");
+        mState = PLAYING;
+        mAutoPaused = false;
+        mAudioTrack->start();
+    }
+}
+
+void SoundChannel::setRate(float rate)
+{
+    Mutex::Autolock lock(&mLock);
+    if (mAudioTrack != NULL && mSample != 0) {
+        uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
+        mAudioTrack->setSampleRate(sampleRate);
+        mRate = rate;
+    }
+}
+
+// call with lock held
+void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
+{
+    mLeftVolume = leftVolume;
+    mRightVolume = rightVolume;
+    if (mAudioTrack != NULL)
+        mAudioTrack->setVolume(leftVolume, rightVolume);
+}
+
+void SoundChannel::setVolume(float leftVolume, float rightVolume)
+{
+    Mutex::Autolock lock(&mLock);
+    setVolume_l(leftVolume, rightVolume);
+}
+
+void SoundChannel::setLoop(int loop)
+{
+    Mutex::Autolock lock(&mLock);
+    if (mAudioTrack != NULL && mSample != 0) {
+        uint32_t loopEnd = mSample->size()/mNumChannels/
+            ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
+        mAudioTrack->setLoop(0, loopEnd, loop);
+        mLoop = loop;
+    }
+}
+
+SoundChannel::~SoundChannel()
+{
+    ALOGV("SoundChannel destructor %p", this);
+    {
+        Mutex::Autolock lock(&mLock);
+        clearNextEvent();
+        doStop_l();
+    }
+    // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack
+    // callback thread to exit which may need to execute process() and acquire the mLock.
+    mAudioTrack.clear();
+}
+
+void SoundChannel::dump()
+{
+    ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d",
+            mState, mChannelID, mNumChannels, mPos, mPriority, mLoop);
+}
+
+void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume,
+            float rightVolume, int priority, int loop, float rate)
+{
+    mSample = sample;
+    mChannelID = channelID;
+    mLeftVolume = leftVolume;
+    mRightVolume = rightVolume;
+    mPriority = priority;
+    mLoop = loop;
+    mRate =rate;
+}
+
+} // end namespace android
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
new file mode 100644
index 0000000..9d9cbdf
--- /dev/null
+++ b/media/jni/soundpool/SoundPool.h
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#ifndef SOUNDPOOL_H_
+#define SOUNDPOOL_H_
+
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <media/AudioTrack.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+
+namespace android {
+
+static const int IDLE_PRIORITY = -1;
+
+// forward declarations
+class SoundEvent;
+class SoundPoolThread;
+class SoundPool;
+
+// for queued events
+class SoundPoolEvent {
+public:
+    SoundPoolEvent(int msg, int arg1=0, int arg2=0) :
+        mMsg(msg), mArg1(arg1), mArg2(arg2) {}
+    int         mMsg;
+    int         mArg1;
+    int         mArg2;
+    enum MessageType { INVALID, SAMPLE_LOADED };
+};
+
+// callback function prototype
+typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
+
+// tracks samples used by application
+class Sample  : public RefBase {
+public:
+    enum sample_state { UNLOADED, LOADING, READY, UNLOADING };
+    Sample(int sampleID, int fd, int64_t offset, int64_t length);
+    ~Sample();
+    int sampleID() { return mSampleID; }
+    int numChannels() { return mNumChannels; }
+    int sampleRate() { return mSampleRate; }
+    audio_format_t format() { return mFormat; }
+    size_t size() { return mSize; }
+    int state() { return mState; }
+    uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
+    status_t doLoad();
+    void startLoad() { mState = LOADING; }
+    sp<IMemory> getIMemory() { return mData; }
+
+private:
+    void init();
+
+    size_t              mSize;
+    volatile int32_t    mRefCount;
+    uint16_t            mSampleID;
+    uint16_t            mSampleRate;
+    uint8_t             mState : 3;
+    uint8_t             mNumChannels : 2;
+    audio_format_t      mFormat;
+    int                 mFd;
+    int64_t             mOffset;
+    int64_t             mLength;
+    sp<IMemory>         mData;
+    sp<MemoryHeapBase>  mHeap;
+};
+
+// stores pending events for stolen channels
+class SoundEvent
+{
+public:
+    SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0),
+            mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {}
+    void set(const sp<Sample>& sample, int channelID, float leftVolume,
+            float rightVolume, int priority, int loop, float rate);
+    sp<Sample>      sample() { return mSample; }
+    int             channelID() { return mChannelID; }
+    float           leftVolume() { return mLeftVolume; }
+    float           rightVolume() { return mRightVolume; }
+    int             priority() { return mPriority; }
+    int             loop() { return mLoop; }
+    float           rate() { return mRate; }
+    void            clear() { mChannelID = 0; mSample.clear(); }
+
+protected:
+    sp<Sample>      mSample;
+    int             mChannelID;
+    float           mLeftVolume;
+    float           mRightVolume;
+    int             mPriority;
+    int             mLoop;
+    float           mRate;
+};
+
+// for channels aka AudioTracks
+class SoundChannel : public SoundEvent {
+public:
+    enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
+    SoundChannel() : mState(IDLE), mNumChannels(1),
+            mPos(0), mToggle(0), mAutoPaused(false) {}
+    ~SoundChannel();
+    void init(SoundPool* soundPool);
+    void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume,
+            int priority, int loop, float rate);
+    void setVolume_l(float leftVolume, float rightVolume);
+    void setVolume(float leftVolume, float rightVolume);
+    void stop_l();
+    void stop();
+    void pause();
+    void autoPause();
+    void resume();
+    void autoResume();
+    void setRate(float rate);
+    int state() { return mState; }
+    void setPriority(int priority) { mPriority = priority; }
+    void setLoop(int loop);
+    int numChannels() { return mNumChannels; }
+    void clearNextEvent() { mNextEvent.clear(); }
+    void nextEvent();
+    int nextChannelID() { return mNextEvent.channelID(); }
+    void dump();
+
+private:
+    static void callback(int event, void* user, void *info);
+    void process(int event, void *info, unsigned long toggle);
+    bool doStop_l();
+
+    SoundPool*          mSoundPool;
+    sp<AudioTrack>      mAudioTrack;
+    SoundEvent          mNextEvent;
+    Mutex               mLock;
+    int                 mState;
+    int                 mNumChannels;
+    int                 mPos;
+    int                 mAudioBufferSize;
+    unsigned long       mToggle;
+    bool                mAutoPaused;
+};
+
+// application object for managing a pool of sounds
+class SoundPool {
+    friend class SoundPoolThread;
+    friend class SoundChannel;
+public:
+    SoundPool(int maxChannels, const audio_attributes_t* pAttributes);
+    ~SoundPool();
+    int load(int fd, int64_t offset, int64_t length, int priority);
+    bool unload(int sampleID);
+    int play(int sampleID, float leftVolume, float rightVolume, int priority,
+            int loop, float rate);
+    void pause(int channelID);
+    void autoPause();
+    void resume(int channelID);
+    void autoResume();
+    void stop(int channelID);
+    void setVolume(int channelID, float leftVolume, float rightVolume);
+    void setPriority(int channelID, int priority);
+    void setLoop(int channelID, int loop);
+    void setRate(int channelID, float rate);
+    const audio_attributes_t* attributes() { return &mAttributes; }
+
+    // called from SoundPoolThread
+    void sampleLoaded(int sampleID);
+
+    // called from AudioTrack thread
+    void done_l(SoundChannel* channel);
+
+    // callback function
+    void setCallback(SoundPoolCallback* callback, void* user);
+    void* getUserData() { return mUserData; }
+
+private:
+    SoundPool() {} // no default constructor
+    bool startThreads();
+    void doLoad(sp<Sample>& sample);
+    sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); }
+    SoundChannel* findChannel (int channelID);
+    SoundChannel* findNextChannel (int channelID);
+    SoundChannel* allocateChannel_l(int priority);
+    void moveToFront_l(SoundChannel* channel);
+    void notify(SoundPoolEvent event);
+    void dump();
+
+    // restart thread
+    void addToRestartList(SoundChannel* channel);
+    void addToStopList(SoundChannel* channel);
+    static int beginThread(void* arg);
+    int run();
+    void quit();
+
+    Mutex                   mLock;
+    Mutex                   mRestartLock;
+    Condition               mCondition;
+    SoundPoolThread*        mDecodeThread;
+    SoundChannel*           mChannelPool;
+    List<SoundChannel*>     mChannels;
+    List<SoundChannel*>     mRestart;
+    List<SoundChannel*>     mStop;
+    DefaultKeyedVector< int, sp<Sample> >   mSamples;
+    int                     mMaxChannels;
+    audio_attributes_t      mAttributes;
+    int                     mAllocated;
+    int                     mNextSampleID;
+    int                     mNextChannelID;
+    bool                    mQuit;
+
+    // callback
+    Mutex                   mCallbackLock;
+    SoundPoolCallback*      mCallback;
+    void*                   mUserData;
+};
+
+} // end namespace android
+
+#endif /*SOUNDPOOL_H_*/
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
new file mode 100644
index 0000000..ba3b482
--- /dev/null
+++ b/media/jni/soundpool/SoundPoolThread.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPoolThread"
+#include "utils/Log.h"
+
+#include "SoundPoolThread.h"
+
+namespace android {
+
+void SoundPoolThread::write(SoundPoolMsg msg) {
+    Mutex::Autolock lock(&mLock);
+    while (mMsgQueue.size() >= maxMessages) {
+        mCondition.wait(mLock);
+    }
+
+    // if thread is quitting, don't add to queue
+    if (mRunning) {
+        mMsgQueue.push(msg);
+        mCondition.signal();
+    }
+}
+
+const SoundPoolMsg SoundPoolThread::read() {
+    Mutex::Autolock lock(&mLock);
+    while (mMsgQueue.size() == 0) {
+        mCondition.wait(mLock);
+    }
+    SoundPoolMsg msg = mMsgQueue[0];
+    mMsgQueue.removeAt(0);
+    mCondition.signal();
+    return msg;
+}
+
+void SoundPoolThread::quit() {
+    Mutex::Autolock lock(&mLock);
+    if (mRunning) {
+        mRunning = false;
+        mMsgQueue.clear();
+        mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
+        mCondition.signal();
+        mCondition.wait(mLock);
+    }
+    ALOGV("return from quit");
+}
+
+SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
+    mSoundPool(soundPool)
+{
+    mMsgQueue.setCapacity(maxMessages);
+    if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
+        mRunning = true;
+    }
+}
+
+SoundPoolThread::~SoundPoolThread()
+{
+    quit();
+}
+
+int SoundPoolThread::beginThread(void* arg) {
+    ALOGV("beginThread");
+    SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg;
+    return soundPoolThread->run();
+}
+
+int SoundPoolThread::run() {
+    ALOGV("run");
+    for (;;) {
+        SoundPoolMsg msg = read();
+        ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
+        switch (msg.mMessageType) {
+        case SoundPoolMsg::KILL:
+            ALOGV("goodbye");
+            return NO_ERROR;
+        case SoundPoolMsg::LOAD_SAMPLE:
+            doLoadSample(msg.mData);
+            break;
+        default:
+            ALOGW("run: Unrecognized message %d\n",
+                    msg.mMessageType);
+            break;
+        }
+    }
+}
+
+void SoundPoolThread::loadSample(int sampleID) {
+    write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
+}
+
+void SoundPoolThread::doLoadSample(int sampleID) {
+    sp <Sample> sample = mSoundPool->findSample(sampleID);
+    status_t status = -1;
+    if (sample != 0) {
+        status = sample->doLoad();
+    }
+    mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
+}
+
+} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
new file mode 100644
index 0000000..d388388
--- /dev/null
+++ b/media/jni/soundpool/SoundPoolThread.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef SOUNDPOOLTHREAD_H_
+#define SOUNDPOOLTHREAD_H_
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include <media/AudioTrack.h>
+
+#include "SoundPool.h"
+
+namespace android {
+
+class SoundPoolMsg {
+public:
+    enum MessageType { INVALID, KILL, LOAD_SAMPLE };
+    SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
+    SoundPoolMsg(MessageType MessageType, int data) :
+        mMessageType(MessageType), mData(data) {}
+    uint16_t         mMessageType;
+    uint16_t         mData;
+};
+
+/*
+ * This class handles background requests from the SoundPool
+ */
+class SoundPoolThread {
+public:
+    SoundPoolThread(SoundPool* SoundPool);
+    ~SoundPoolThread();
+    void loadSample(int sampleID);
+    void quit();
+    void write(SoundPoolMsg msg);
+
+private:
+    static const size_t maxMessages = 5;
+
+    static int beginThread(void* arg);
+    int run();
+    void doLoadSample(int sampleID);
+    const SoundPoolMsg read();
+
+    Mutex                   mLock;
+    Condition               mCondition;
+    Vector<SoundPoolMsg>    mMsgQueue;
+    SoundPool*              mSoundPool;
+    bool                    mRunning;
+};
+
+} // end namespace android
+
+#endif /*SOUNDPOOLTHREAD_H_*/
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index baf61d5..b2333f8 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -23,7 +23,7 @@
 #include <nativehelper/jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <media/SoundPool.h>
+#include "SoundPool.h"
 
 using namespace android;
 
@@ -45,20 +45,6 @@
 static audio_attributes_fields_t javaAudioAttrFields;
 
 // ----------------------------------------------------------------------------
-static jint
-android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
-{
-    ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL");
-    SoundPool *ap = MusterSoundPool(env, thiz);
-    if (path == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return 0;
-    }
-    const char* s = env->GetStringUTFChars(path, NULL);
-    int id = ap->load(s, priority);
-    env->ReleaseStringUTFChars(path, s);
-    return (jint) id;
-}
 
 static jint
 android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
@@ -249,10 +235,6 @@
 // Dalvik VM type signatures
 static JNINativeMethod gMethods[] = {
     {   "_load",
-        "(Ljava/lang/String;I)I",
-        (void *)android_media_SoundPool_SoundPoolImpl_load_URL
-    },
-    {   "_load",
         "(Ljava/io/FileDescriptor;JJI)I",
         (void *)android_media_SoundPool_SoundPoolImpl_load_FD
     },
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index c2bb90c..0b7b99f 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -29,7 +29,8 @@
 import android.os.ServiceManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
-import android.util.Log;
+import android.text.Editable;
+import android.text.TextWatcher;
 import android.util.Slog;
 import android.view.View;
 import android.widget.Button;
@@ -180,20 +181,6 @@
         mEncPassword = (TextView) findViewById(R.id.enc_password);
         TextView curPwDesc = (TextView) findViewById(R.id.password_desc);
 
-        // We vary the password prompt depending on whether one is predefined, and whether
-        // the device is encrypted.
-        mIsEncrypted = deviceIsEncrypted();
-        if (!haveBackupPassword()) {
-            curPwDesc.setVisibility(View.GONE);
-            mCurPassword.setVisibility(View.GONE);
-            if (layoutId == R.layout.confirm_backup) {
-                TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc);
-                encPwDesc.setText(mIsEncrypted
-                                  ? R.string.backup_enc_password_required
-                                  : R.string.backup_enc_password_optional);
-            }
-        }
-
         mAllowButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -209,6 +196,7 @@
                 sendAcknowledgement(mToken, false, mObserver);
                 mAllowButton.setEnabled(false);
                 mDenyButton.setEnabled(false);
+                finish();
             }
         });
 
@@ -218,6 +206,39 @@
             mAllowButton.setEnabled(!mDidAcknowledge);
             mDenyButton.setEnabled(!mDidAcknowledge);
         }
+
+        // We vary the password prompt depending on whether one is predefined, and whether
+        // the device is encrypted.
+        mIsEncrypted = deviceIsEncrypted();
+        if (!haveBackupPassword()) {
+            curPwDesc.setVisibility(View.GONE);
+            mCurPassword.setVisibility(View.GONE);
+            if (layoutId == R.layout.confirm_backup) {
+                TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc);
+                if (mIsEncrypted) {
+                    encPwDesc.setText(R.string.backup_enc_password_required);
+                    monitorEncryptionPassword();
+                } else {
+                    encPwDesc.setText(R.string.backup_enc_password_optional);
+                }
+            }
+        }
+    }
+
+    private void monitorEncryptionPassword() {
+        mAllowButton.setEnabled(false);
+        mEncPassword.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) { }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                mAllowButton.setEnabled(mEncPassword.getText().length() > 0);
+            }
+        });
     }
 
     // Preserve the restore observer callback binder across activity relaunch
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7cee066..5e9ec10 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -97,14 +97,18 @@
 
     /** Called on mLooper thread */
     public void enable() {
-        mEnabled = true;
-        updateRequirements();
+        if (!mEnabled) {
+            mEnabled = true;
+            updateRequirements();
+        }
     }
 
     /** Called on mLooper thread */
     public void disable() {
-        mEnabled = false;
-        updateRequirements();
+        if (mEnabled) {
+            mEnabled = false;
+            updateRequirements();
+        }
     }
 
     /** Called on mLooper thread */
@@ -131,16 +135,14 @@
     private void enableProvider(String name, long minTime) {
         ProviderStats stats = mStats.get(name);
 
-        if (stats.available) {
-            if (!stats.requested) {
-                stats.requestTime = SystemClock.elapsedRealtime();
-                stats.requested = true;
-                stats.minTime = minTime;
-                mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
-            } else if (stats.minTime != minTime) {
-                stats.minTime = minTime;
-                mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
-            }
+        if (!stats.requested) {
+            stats.requestTime = SystemClock.elapsedRealtime();
+            stats.requested = true;
+            stats.minTime = minTime;
+            mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
+        } else if (stats.minTime != minTime) {
+            stats.minTime = minTime;
+            mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
         }
     }
 
diff --git a/packages/Keyguard/res/layout/keyguard_host_view.xml b/packages/Keyguard/res/layout/keyguard_host_view.xml
index 3635aff..7291cd4 100644
--- a/packages/Keyguard/res/layout/keyguard_host_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_host_view.xml
@@ -26,7 +26,9 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="false"
-    android:clipToPadding="false">
+    android:clipToPadding="false"
+    android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
+                                                  from this view when bouncer is shown -->
 
     <com.android.keyguard.KeyguardSecurityContainer
         android:id="@+id/keyguard_security_container"
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index c5d61ac..9f9f970 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -34,7 +34,7 @@
     <string name="keyguard_low_battery" msgid="8143808018719173859">"请连接充电器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按“菜单”键解锁。"</string>
     <string name="keyguard_network_locked_message" msgid="9169717779058037168">"网络已锁定"</string>
-    <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"无SIM卡"</string>
+    <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"无 SIM 卡"</string>
     <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"平板电脑中没有SIM卡。"</string>
     <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"手机中没有SIM卡。"</string>
     <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"请插入SIM卡。"</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index d8b0c71..c023dc6 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -41,7 +41,8 @@
 
     private static CharSequence mSeparator;
 
-    private LockPatternUtils mLockPatternUtils;
+    private final boolean mIsEmergencyCallCapable;
+
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
@@ -78,7 +79,8 @@
 
     public CarrierText(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLockPatternUtils = new LockPatternUtils(mContext);
+        mIsEmergencyCallCapable = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
         boolean useAllCaps;
         TypedArray a = context.getTheme().obtainStyledAttributes(
                 attrs, R.styleable.CarrierText, 0, 0);
@@ -222,7 +224,7 @@
      */
     private CharSequence makeCarrierStringOnEmergencyCapable(
             CharSequence simMessage, CharSequence emergencyCallMessage) {
-        if (mLockPatternUtils.isEmergencyCallCapable()) {
+        if (mIsEmergencyCallCapable) {
             return concatenate(simMessage, emergencyCallMessage);
         }
         return simMessage;
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 50ac261..3627e3e 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -21,7 +21,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.telephony.TelephonyManager;
+import android.telecom.TelecomManager;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.Button;
@@ -50,8 +50,17 @@
             updateEmergencyCallButton();
         }
     };
+
+    public interface EmergencyButtonCallback {
+        public void onEmergencyButtonClickedWhenInCall();
+    }
+
     private LockPatternUtils mLockPatternUtils;
     private PowerManager mPowerManager;
+    private EmergencyButtonCallback mEmergencyButtonCallback;
+
+    private final boolean mIsVoiceCapable;
+    private final boolean mEnableEmergencyCallWhileSimLocked;
 
     public EmergencyButton(Context context) {
         this(context, null);
@@ -59,6 +68,10 @@
 
     public EmergencyButton(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mIsVoiceCapable = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
+        mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
     }
 
     @Override
@@ -93,8 +106,11 @@
         // TODO: implement a shorter timeout once new PowerManager API is ready.
         // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT)
         mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
-        if (mLockPatternUtils.isInCall()) {
-            mLockPatternUtils.resumeCall();
+        if (isInCall()) {
+            resumeCall();
+            if (mEmergencyButtonCallback != null) {
+                mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
+            }
         } else {
             final boolean bypassHandler = true;
             KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(bypassHandler);
@@ -107,21 +123,57 @@
     }
 
     private void updateEmergencyCallButton() {
-        boolean enabled = false;
-        if (mLockPatternUtils.isInCall()) {
-            enabled = true; // always show "return to call" if phone is off-hook
-        } else if (mLockPatternUtils.isEmergencyCallCapable()) {
-            final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimPinVoiceSecure();
-            if (simLocked) {
-                // Some countries can't handle emergency calls while SIM is locked.
-                enabled = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked();
+        boolean visible = false;
+        if (mIsVoiceCapable) {
+            // Emergency calling requires voice capability.
+            if (isInCall()) {
+                visible = true; // always show "return to call" if phone is off-hook
             } else {
-                // True if we need to show a secure screen (pin/pattern/SIM pin/SIM puk);
-                // hides emergency button on "Slide" screen if device is not secure.
-                enabled = mLockPatternUtils.isSecure();
+                final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext)
+                        .isSimPinVoiceSecure();
+                if (simLocked) {
+                    // Some countries can't handle emergency calls while SIM is locked.
+                    visible = mEnableEmergencyCallWhileSimLocked;
+                } else {
+                    // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk);
+                    visible = mLockPatternUtils.isSecure();
+                }
             }
         }
-        mLockPatternUtils.updateEmergencyCallButtonState(this, enabled, false);
+        if (visible) {
+            setVisibility(View.VISIBLE);
+
+            int textId;
+            if (isInCall()) {
+                textId = com.android.internal.R.string.lockscreen_return_to_call;
+            } else {
+                textId = com.android.internal.R.string.lockscreen_emergency_call;
+            }
+            setText(textId);
+        } else {
+            setVisibility(View.GONE);
+        }
     }
 
+    public void setCallback(EmergencyButtonCallback callback) {
+        mEmergencyButtonCallback = callback;
+    }
+
+    /**
+     * Resumes a call in progress.
+     */
+    private void resumeCall() {
+        getTelecommManager().showInCallScreen(false);
+    }
+
+    /**
+     * @return {@code true} if there is a call currently in progress.
+     */
+    private boolean isInCall() {
+        return getTelecommManager().isInCall();
+    }
+
+    private TelecomManager getTelecommManager() {
+        return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index b03176c..b2e4728 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -32,7 +32,7 @@
  * Base class for PIN and password unlock screens.
  */
 public abstract class KeyguardAbsKeyInputView extends LinearLayout
-        implements KeyguardSecurityView {
+        implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
     protected KeyguardSecurityCallback mCallback;
     protected LockPatternUtils mLockPatternUtils;
     protected SecurityMessageDisplay mSecurityMessageDisplay;
@@ -85,6 +85,15 @@
         mLockPatternUtils = new LockPatternUtils(mContext);
         mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
         mEcaView = findViewById(R.id.keyguard_selector_fade_container);
+
+        EmergencyButton button = (EmergencyButton) findViewById(R.id.emergency_call_button);
+        if (button != null) {
+            button.setCallback(this);
+        }
+    }
+
+    public void onEmergencyButtonClickedWhenInCall() {
+        mCallback.reset();
     }
 
     /*
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index c7bc04d..10ce426 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -153,6 +154,16 @@
         return false;
     }
 
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription());
+            return true;
+        } else {
+            return super.dispatchPopulateAccessibilityEvent(event);
+        }
+    }
+
     protected KeyguardSecurityContainer getSecurityContainer() {
         return mSecurityContainer;
     }
@@ -185,6 +196,11 @@
     }
 
     @Override
+    public void reset() {
+        mViewMediatorCallback.resetKeyguard();
+    }
+
+    @Override
     public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
         if (mViewMediatorCallback != null) {
             mViewMediatorCallback.setNeedsInput(needsInput);
@@ -416,6 +432,4 @@
     public SecurityMode getCurrentSecurityMode() {
         return mSecurityContainer.getCurrentSecurityMode();
     }
-
-
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 62f1b69..9aa5729 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -38,7 +38,8 @@
 import java.util.List;
 
 public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
-        AppearAnimationCreator<LockPatternView.CellState> {
+        AppearAnimationCreator<LockPatternView.CellState>,
+        EmergencyButton.EmergencyButtonCallback {
 
     private static final String TAG = "SecurityPatternView";
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -140,6 +141,15 @@
         mEcaView = findViewById(R.id.keyguard_selector_fade_container);
         mContainer = (ViewGroup) findViewById(R.id.container);
         mHelpMessage = (KeyguardMessageArea) findViewById(R.id.keyguard_message_area);
+
+        EmergencyButton button = (EmergencyButton) findViewById(R.id.emergency_call_button);
+        if (button != null) {
+            button.setCallback(this);
+        }
+    }
+
+    public void onEmergencyButtonClickedWhenInCall() {
+        mCallback.reset();
     }
 
     @Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
index 8bb8805..5877bc8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -40,4 +40,8 @@
      */
     void reportUnlockAttempt(boolean success);
 
+    /**
+     * Resets the keyguard view.
+     */
+    void reset();
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 61eef48..41ec3b0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -55,6 +55,7 @@
         public void userActivity();
         public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
         public void finish();
+        public void reset();
     }
 
     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
@@ -111,6 +112,14 @@
         }
     }
 
+    public CharSequence getCurrentSecurityModeContentDescription() {
+        View v = (View) getSecurityView(mCurrentSecuritySelection);
+        if (v != null) {
+            return v.getContentDescription();
+        }
+        return "";
+    }
+
     private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
         KeyguardSecurityView view = null;
@@ -406,7 +415,6 @@
     }
 
     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
-
         public void userActivity() {
             if (mSecurityCallback != null) {
                 mSecurityCallback.userActivity();
@@ -431,6 +439,9 @@
             }
         }
 
+        public void reset() {
+            mSecurityCallback.reset();
+        }
     };
 
     // The following is used to ignore callbacks from SecurityViews that are no longer current
@@ -445,6 +456,8 @@
         public boolean isVerifyUnlockOnly() { return false; }
         @Override
         public void dismiss(boolean securityVerified) { }
+        @Override
+        public void reset() {}
     };
 
     private int getSecurityViewIdForMode(SecurityMode securityMode) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
index 107f417..3eb31ad 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -17,20 +17,16 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.widget.LockPatternUtils;
 
-import java.util.List;
-
 public class KeyguardSecurityModel {
 
     /**
-     * The different types of security available for {@link Mode#UnlockScreen}.
-     * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
+     * The different types of security available.
+     * @see KeyguardSecurityContainer#showSecurityScreen
      */
     public enum SecurityMode {
         Invalid, // NULL state
@@ -42,12 +38,16 @@
         SimPuk // Unlock by entering a sim puk
     }
 
-    private Context mContext;
+    private final Context mContext;
+    private final boolean mIsPukScreenAvailable;
+
     private LockPatternUtils mLockPatternUtils;
 
     KeyguardSecurityModel(Context context) {
         mContext = context;
         mLockPatternUtils = new LockPatternUtils(context);
+        mIsPukScreenAvailable = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enable_puk_unlock_screen);
     }
 
     void setLockPatternUtils(LockPatternUtils utils) {
@@ -56,39 +56,35 @@
 
     SecurityMode getSecurityMode() {
         KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
-        SecurityMode mode = SecurityMode.None;
+
         if (SubscriptionManager.isValidSubscriptionId(
                 monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) {
-            mode = SecurityMode.SimPin;
-        } else if (SubscriptionManager.isValidSubscriptionId(
-                    monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))
-                && mLockPatternUtils.isPukUnlockScreenEnable()) {
-            mode = SecurityMode.SimPuk;
-        } else {
-            final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();
-            switch (security) {
-                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
-                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
-                    mode = mLockPatternUtils.isLockPasswordEnabled() ?
-                            SecurityMode.PIN : SecurityMode.None;
-                    break;
-                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
-                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
-                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
-                    mode = mLockPatternUtils.isLockPasswordEnabled() ?
-                            SecurityMode.Password : SecurityMode.None;
-                    break;
-
-                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
-                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
-                    mode = mLockPatternUtils.isLockPatternEnabled() ?
-                            SecurityMode.Pattern : SecurityMode.None;
-                    break;
-
-                default:
-                    throw new IllegalStateException("Unknown security quality:" + security);
-            }
+            return SecurityMode.SimPin;
         }
-        return mode;
+
+        if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
+                monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) {
+            return SecurityMode.SimPuk;
+        }
+
+        final int security = mLockPatternUtils.getActivePasswordQuality();
+        switch (security) {
+            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
+                return SecurityMode.PIN;
+
+            case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+            case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+            case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+                return SecurityMode.Password;
+
+            case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+                return SecurityMode.Pattern;
+            case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+                return SecurityMode.None;
+
+            default:
+                throw new IllegalStateException("Unknown security quality:" + security);
+        }
     }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
index f327078..5bbcc8c 100644
--- a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
@@ -62,6 +62,11 @@
     void readyForKeyguardDone();
 
     /**
+     * Reset the keyguard and bouncer.
+     */
+    void resetKeyguard();
+
+    /**
      * Play the "device trusted" sound.
      */
     void playTrustedSound();
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 0bf64aa..a87afe0 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -159,6 +159,34 @@
                 android:layout_marginEnd="16dip"
                 android:orientation="vertical">
 
+                <!-- Duplex -->
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="8dip"
+                    android:layout_marginStart="12dip"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:labelFor="@+id/duplex_spinner"
+                    android:text="@string/label_duplex">
+                </TextView>
+
+                <Spinner
+                    android:id="@+id/duplex_spinner"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="4dip">
+                </Spinner>
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:layout_marginEnd="16dip"
+                android:orientation="vertical">
+
                 <!-- Range options -->
 
                 <TextView
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 1a840e3..923a38c 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papiergrootte"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papiergrootte:"</string>
     <string name="label_color" msgid="1108690305218188969">"Kleur"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Oriëntasie"</string>
     <string name="label_pages" msgid="7768589729282182230">"Bladsye"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Al <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Swart en wit"</item>
     <item msgid="2762241247228983754">"Kleur"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Geen"</item>
+    <item msgid="7296563835355641719">"Lang rand"</item>
+    <item msgid="79513688117503758">"Kort rand"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portret"</item>
     <item msgid="3199660090246166812">"Landskap"</item>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index 683d585..8f46249 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"የወረቀት መጠን"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"የወረቀት መጠን፦"</string>
     <string name="label_color" msgid="1108690305218188969">"ቀለም"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"አቀማመጠ ገፅ"</string>
     <string name="label_pages" msgid="7768589729282182230">"ገፆች"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"ሁሉም <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -33,7 +35,7 @@
     <string name="install_for_print_preview" msgid="6366303997385509332">"ለቅድመ-እይታ የፒ ዲ ኤፍ መመልከቻ ይጫኑ"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"የአታሚ መተግበሪያ ተበላሽቷል"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"የህትመት ስራን በማመንጨት ላይ"</string>
-    <string name="save_as_pdf" msgid="5718454119847596853">"እንደ ፒዲኤፍ አስቀምጥ"</string>
+    <string name="save_as_pdf" msgid="5718454119847596853">"እንደ PDF አስቀምጥ"</string>
     <string name="all_printers" msgid="5018829726861876202">"ሁሉም አታሚዎች…"</string>
     <string name="print_dialog" msgid="32628687461331979">"የህትመት መገናኛ"</string>
     <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"ጥቁር እና ነጭ"</item>
     <item msgid="2762241247228983754">"ቀለም"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"ምንም"</item>
+    <item msgid="7296563835355641719">"ረጅም ጠርዝ"</item>
+    <item msgid="79513688117503758">"አጭር ጠርዝ"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"የቁም"</item>
     <item msgid="3199660090246166812">"የወርድ"</item>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index 0a7d301..9f931d5 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"حجم الورق"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"حجم الورق:"</string>
     <string name="label_color" msgid="1108690305218188969">"ألوان"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"الاتجاه"</string>
     <string name="label_pages" msgid="7768589729282182230">"الصفحات"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"جميع الصفحات وعددها <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"أبيض وأسود"</item>
     <item msgid="2762241247228983754">"ملونة"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"لا شيء"</item>
+    <item msgid="7296563835355641719">"حافة طويلة"</item>
+    <item msgid="79513688117503758">"حافة قصيرة"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"عمودي"</item>
     <item msgid="3199660090246166812">"أفقي"</item>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index 8792349..981b4c1 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Размер на хартията"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Размер на хартията:"</string>
     <string name="label_color" msgid="1108690305218188969">"Цвят"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
     <string name="label_pages" msgid="7768589729282182230">"Страници"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Всички <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Черно-бяло"</item>
     <item msgid="2762241247228983754">"Цветно"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Без"</item>
+    <item msgid="7296563835355641719">"Дълъг ръб"</item>
+    <item msgid="79513688117503758">"Къс ръб"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Вертикално"</item>
     <item msgid="3199660090246166812">"Хоризонтално"</item>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index 5c5fabf..4d74ceb 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"কাগজের আকার"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"কাগজের আকার:"</string>
     <string name="label_color" msgid="1108690305218188969">"রঙ"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"সজ্জা"</string>
     <string name="label_pages" msgid="7768589729282182230">"পৃষ্ঠাগুলি"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"সমস্ত <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"কালো এবং সাদা"</item>
     <item msgid="2762241247228983754">"রঙ"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"প্রতিকৃতি"</item>
     <item msgid="3199660090246166812">"ভূদৃশ্য"</item>
diff --git a/packages/PrintSpooler/res/values-ca/arrays.xml b/packages/PrintSpooler/res/values-ca/arrays.xml
new file mode 100644
index 0000000..c1b149c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-ca/arrays.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+
+    <string-array name="pdf_printer_media_sizes" translatable="false">
+        <item>NA_LETTER</item>
+        <item>NA_GOVT_LETTER</item>
+        <item>NA_LEGAL</item>
+        <item>NA_JUNIOR_LEGAL</item>
+        <item>NA_LEDGER</item>
+        <item>NA_TABLOID</item>
+        <item>NA_INDEX_3X5</item>
+        <item>NA_INDEX_4X6</item>
+        <item>NA_INDEX_5X8</item>
+        <item>NA_MONARCH</item>
+        <item>NA_QUARTO</item>
+        <item>NA_FOOLSCAP</item>
+    </string-array>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index a9677cb..ca7bafa 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Mida del paper"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Mida del paper:"</string>
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientació"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pàgines"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Totes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Blanc i negre"</item>
     <item msgid="2762241247228983754">"Color"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Cap"</item>
+    <item msgid="7296563835355641719">"Costat llarg"</item>
+    <item msgid="79513688117503758">"Costat curt"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Vertical"</item>
     <item msgid="3199660090246166812">"Horitzontal"</item>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 791e485..77f338a 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Velikost papíru"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Velikost papíru:"</string>
     <string name="label_color" msgid="1108690305218188969">"Barva"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientace"</string>
     <string name="label_pages" msgid="7768589729282182230">"Stránky"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Vše: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Černobíle"</item>
     <item msgid="2762241247228983754">"Barevně"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Žádné"</item>
+    <item msgid="7296563835355641719">"Dlouhý okraj"</item>
+    <item msgid="79513688117503758">"Krátký okraj"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Na výšku"</item>
     <item msgid="3199660090246166812">"Na šířku"</item>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index c4a383e..b497c18 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papirstørrelse"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papirstørrelse:"</string>
     <string name="label_color" msgid="1108690305218188969">"Farve"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Retning"</string>
     <string name="label_pages" msgid="7768589729282182230">"Sider"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Sort/hvid"</item>
     <item msgid="2762241247228983754">"Farve"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ingen"</item>
+    <item msgid="7296563835355641719">"Den lange led"</item>
+    <item msgid="79513688117503758">"Den korte led"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Stående"</item>
     <item msgid="3199660090246166812">"Liggende"</item>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index 43be3dc..1b9ce09 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papierformat"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papierformat:"</string>
     <string name="label_color" msgid="1108690305218188969">"Farbe"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Ausrichtung"</string>
     <string name="label_pages" msgid="7768589729282182230">"Seiten"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Schwarz-weiß"</item>
     <item msgid="2762241247228983754">"Farbe"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ohne"</item>
+    <item msgid="7296563835355641719">"Lange Seite"</item>
+    <item msgid="79513688117503758">"Kurze Seite"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Hochformat"</item>
     <item msgid="3199660090246166812">"Querformat"</item>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index 25f609b..2908b6a 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Μεγέθος χαρτιού"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Μέγεθος χαρτιού:"</string>
     <string name="label_color" msgid="1108690305218188969">"Χρώμα"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Προσανατολισμός"</string>
     <string name="label_pages" msgid="7768589729282182230">"Σελίδες"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Και οι <xliff:g id="PAGE_COUNT">%1$s</xliff:g> σελίδες"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Ασπρόμαυρο"</item>
     <item msgid="2762241247228983754">"Χρώμα"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Κανένα"</item>
+    <item msgid="7296563835355641719">"Μεγάλη πλευρά"</item>
+    <item msgid="79513688117503758">"Μικρή πλευρά"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Πορτραίτο"</item>
     <item msgid="3199660090246166812">"Οριζόντια"</item>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 2198c7a..5fa5338 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Paper size"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Paper size:"</string>
     <string name="label_color" msgid="1108690305218188969">"Colour"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pages"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"All <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Black &amp; White"</item>
     <item msgid="2762241247228983754">"Colour"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"None"</item>
+    <item msgid="7296563835355641719">"Long edge"</item>
+    <item msgid="79513688117503758">"Short edge"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portrait"</item>
     <item msgid="3199660090246166812">"Landscape"</item>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 2198c7a..5fa5338 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Paper size"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Paper size:"</string>
     <string name="label_color" msgid="1108690305218188969">"Colour"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pages"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"All <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Black &amp; White"</item>
     <item msgid="2762241247228983754">"Colour"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"None"</item>
+    <item msgid="7296563835355641719">"Long edge"</item>
+    <item msgid="79513688117503758">"Short edge"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portrait"</item>
     <item msgid="3199660090246166812">"Landscape"</item>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index e194f55..17ea709 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Tamaño del papel"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Tamaño de papel:"</string>
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
     <string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Todas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Blanco y negro"</item>
     <item msgid="2762241247228983754">"Color"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ninguno"</item>
+    <item msgid="7296563835355641719">"Borde largo"</item>
+    <item msgid="79513688117503758">"Borde corto"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Vertical"</item>
     <item msgid="3199660090246166812">"Horizontal"</item>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index b0ab529..c597177 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Tamaño del papel"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Tamaño del papel:"</string>
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
     <string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Todas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Blanco y negro"</item>
     <item msgid="2762241247228983754">"Color"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ninguna"</item>
+    <item msgid="7296563835355641719">"Borde largo"</item>
+    <item msgid="79513688117503758">"Borde corto"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Vertical"</item>
     <item msgid="3199660090246166812">"Horizontal"</item>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 6e75bbb..5e300da 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Paberi suurus"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Paberi suurus:"</string>
     <string name="label_color" msgid="1108690305218188969">"Värv"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Suund"</string>
     <string name="label_pages" msgid="7768589729282182230">"Lehed"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Kõik <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Mustvalge"</item>
     <item msgid="2762241247228983754">"Värv"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Puudub"</item>
+    <item msgid="7296563835355641719">"Pikk serv"</item>
+    <item msgid="79513688117503758">"Lühike serv"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Vertikaalpaigutus"</item>
     <item msgid="3199660090246166812">"Horisontaalpaigutus"</item>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 4f0f8fc..4e79c60 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Paperaren tamaina"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Paperaren tamaina:"</string>
     <string name="label_color" msgid="1108690305218188969">"Koloretan"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientazioa"</string>
     <string name="label_pages" msgid="7768589729282182230">"Orriak"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> orriak"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Zuri-beltza"</item>
     <item msgid="2762241247228983754">"Koloretakoa"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Bertikala"</item>
     <item msgid="3199660090246166812">"Horizontala"</item>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index d35a063..1bc934e 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"اندازه کاغذ"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"اندازه کاغذ:"</string>
     <string name="label_color" msgid="1108690305218188969">"رنگی"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"جهت"</string>
     <string name="label_pages" msgid="7768589729282182230">"صفحه‌ها"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"همه <xliff:g id="PAGE_COUNT">%1$s</xliff:g> صفحه"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"سیاه و سفید"</item>
     <item msgid="2762241247228983754">"رنگی"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"هیچ‌کدام"</item>
+    <item msgid="7296563835355641719">"طول"</item>
+    <item msgid="79513688117503758">"عرض"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"عمودی"</item>
     <item msgid="3199660090246166812">"افقی"</item>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index 8e540f9..87dd636 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Paperikoko"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Paperikoko:"</string>
     <string name="label_color" msgid="1108690305218188969">"Väri"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Suunta"</string>
     <string name="label_pages" msgid="7768589729282182230">"Sivut"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Kaikki <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Mustavalkoinen"</item>
     <item msgid="2762241247228983754">"Väri"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ei mitään"</item>
+    <item msgid="7296563835355641719">"Pitkä reuna"</item>
+    <item msgid="79513688117503758">"Lyhyt reuna"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Pysty"</item>
     <item msgid="3199660090246166812">"Vaaka"</item>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 279a467..fe038fd 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Taille du papier"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Taille du papier :"</string>
     <string name="label_color" msgid="1108690305218188969">"Couleur"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pages"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Toutes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Noir et blanc"</item>
     <item msgid="2762241247228983754">"Couleur"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Aucune"</item>
+    <item msgid="7296563835355641719">"Bord long"</item>
+    <item msgid="79513688117503758">"Bord court"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portrait"</item>
     <item msgid="3199660090246166812">"Paysage"</item>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index ebfd5de..23aaf51 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Taille du papier"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Taille du papier :"</string>
     <string name="label_color" msgid="1108690305218188969">"Couleur"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pages"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Toutes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Noir et blanc"</item>
     <item msgid="2762241247228983754">"Couleur"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Aucun"</item>
+    <item msgid="7296563835355641719">"Bord long"</item>
+    <item msgid="79513688117503758">"Bord court"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portrait"</item>
     <item msgid="3199660090246166812">"Paysage"</item>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index 6e542ea..ba8432f 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Tamaño do papel"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Tamaño do papel:"</string>
     <string name="label_color" msgid="1108690305218188969">"Cor"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
     <string name="label_pages" msgid="7768589729282182230">"Páxinas"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"As <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Branco e negro"</item>
     <item msgid="2762241247228983754">"Cor"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Vertical"</item>
     <item msgid="3199660090246166812">"Horizontal"</item>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index a3f7fef..b39efcb 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"काग़ज़ का आकार"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"काग़ज़ का आकार:"</string>
     <string name="label_color" msgid="1108690305218188969">"रंग"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"अभिविन्‍यास"</string>
     <string name="label_pages" msgid="7768589729282182230">"पृष्ठ"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"सभी <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"श्याम और श्वेत"</item>
     <item msgid="2762241247228983754">"रंग"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"कोई नहीं"</item>
+    <item msgid="7296563835355641719">"लंबा सिरा"</item>
+    <item msgid="79513688117503758">"छोटा सिरा"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"पोर्ट्रेट"</item>
     <item msgid="3199660090246166812">"लैंडस्केप"</item>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 8132d21..7e68f09 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Veličina papira"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Veličina papira:"</string>
     <string name="label_color" msgid="1108690305218188969">"U boji"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orijentacija"</string>
     <string name="label_pages" msgid="7768589729282182230">"Stranice"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Sve stranice (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Crno-bijelo"</item>
     <item msgid="2762241247228983754">"U boji"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ništa"</item>
+    <item msgid="7296563835355641719">"Dulji rub"</item>
+    <item msgid="79513688117503758">"Kraći rub"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portret"</item>
     <item msgid="3199660090246166812">"Pejzaž"</item>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 3ebc450..10b351d 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papírméret"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papírméret:"</string>
     <string name="label_color" msgid="1108690305218188969">"Szín"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Tájolás"</string>
     <string name="label_pages" msgid="7768589729282182230">"Oldalak"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Összes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Fekete-fehér"</item>
     <item msgid="2762241247228983754">"Szín"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Nincs"</item>
+    <item msgid="7296563835355641719">"Hosszabb él"</item>
+    <item msgid="79513688117503758">"Rövidebb él"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Álló"</item>
     <item msgid="3199660090246166812">"Fekvő"</item>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 5ef15a5..08cb138 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Թղթի չափը"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Թղթի չափը՝"</string>
     <string name="label_color" msgid="1108690305218188969">"Գույնը"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Դիրքավորում"</string>
     <string name="label_pages" msgid="7768589729282182230">"Էջեր"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Բոլորը՝ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Սև ու սպիտակ"</item>
     <item msgid="2762241247228983754">"Գույնը"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ոչ մեկը"</item>
+    <item msgid="7296563835355641719">"Լայնեզր"</item>
+    <item msgid="79513688117503758">"Կարճեզր"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Դիմանկար"</item>
     <item msgid="3199660090246166812">"Լանդշաֆտ"</item>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 9714b0f..666442c 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Ukuran kertas"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Ukuran kertas:"</string>
     <string name="label_color" msgid="1108690305218188969">"Warna"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
     <string name="label_pages" msgid="7768589729282182230">"Halaman"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Semua dari <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Hitam &amp; Putih"</item>
     <item msgid="2762241247228983754">"Warna"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Tidak ada"</item>
+    <item msgid="7296563835355641719">"Tepi panjang"</item>
+    <item msgid="79513688117503758">"Tepi pendek"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Potret"</item>
     <item msgid="3199660090246166812">"Lanskap"</item>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index 41a047d..bb0c7ca 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Pappírsstærð"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Pappírsstærð:"</string>
     <string name="label_color" msgid="1108690305218188969">"Litur"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Stefna"</string>
     <string name="label_pages" msgid="7768589729282182230">"Síður"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Allar <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Svarthvítt"</item>
     <item msgid="2762241247228983754">"Í lit"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Skammsnið"</item>
     <item msgid="3199660090246166812">"Langsnið"</item>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 3653c56..39043ee 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Dimensioni carta"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Dimensioni carta:"</string>
     <string name="label_color" msgid="1108690305218188969">"A colori"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientamento"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pagine"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Tutte e <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Bianco e nero"</item>
     <item msgid="2762241247228983754">"A colori"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Nessuno"</item>
+    <item msgid="7296563835355641719">"Lato lungo"</item>
+    <item msgid="79513688117503758">"Lato corto"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Verticale"</item>
     <item msgid="3199660090246166812">"Orizzontale"</item>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index ba293a0..1fd68e9 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"גודל נייר"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"גודל נייר:"</string>
     <string name="label_color" msgid="1108690305218188969">"צבע"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"כיוון"</string>
     <string name="label_pages" msgid="7768589729282182230">"עמודים"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"הכל <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"שחור ולבן"</item>
     <item msgid="2762241247228983754">"צבע"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"ללא"</item>
+    <item msgid="7296563835355641719">"צד ארוך"</item>
+    <item msgid="79513688117503758">"צד קצר"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"לאורך"</item>
     <item msgid="3199660090246166812">"לרוחב"</item>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index f3fed3b..3cc7e8e 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"用紙サイズ"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"用紙サイズ:"</string>
     <string name="label_color" msgid="1108690305218188969">"カラー選択"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"方向"</string>
     <string name="label_pages" msgid="7768589729282182230">"ページ"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>ページすべて"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"モノクロ"</item>
     <item msgid="2762241247228983754">"カラー"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"なし"</item>
+    <item msgid="7296563835355641719">"長辺"</item>
+    <item msgid="79513688117503758">"短辺"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"縦向き"</item>
     <item msgid="3199660090246166812">"横向き"</item>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index 061e9fb..4a55dfe 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"ფურცლის ზომა"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"ფურცლის ზომა:"</string>
     <string name="label_color" msgid="1108690305218188969">"ფერი"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"ორიენტაცია"</string>
     <string name="label_pages" msgid="7768589729282182230">"გვერდები"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"ყველა <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"შავ-თეთრი"</item>
     <item msgid="2762241247228983754">"ფერი"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"არც ერთი"</item>
+    <item msgid="7296563835355641719">"გრძელი კიდე"</item>
+    <item msgid="79513688117503758">"მოკლე კიდე"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"პორტრეტი"</item>
     <item msgid="3199660090246166812">"პეიზაჟის რეჟიმი"</item>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index b02714b..362d790 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Қағаз өлшемі"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Қағаз өлшемі:"</string>
     <string name="label_color" msgid="1108690305218188969">"Түс"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Бағыты"</string>
     <string name="label_pages" msgid="7768589729282182230">"Беттер"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Барлық <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Қара &amp; Ақ"</item>
     <item msgid="2762241247228983754">"Түс"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Портреттік"</item>
     <item msgid="3199660090246166812">"Ландшафт"</item>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index 63d710a..9bc3362 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"ទំហំ​​ក្រដាស"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"ទំហំ​ក្រដាស៖"</string>
     <string name="label_color" msgid="1108690305218188969">"ពណ៌"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"ទិស"</string>
     <string name="label_pages" msgid="7768589729282182230">"ទំព័រ"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ទាំងអស់"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"ស &amp; ខ្មៅ"</item>
     <item msgid="2762241247228983754">"ពណ៌"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"គ្មាន"</item>
+    <item msgid="7296563835355641719">"គែម​វែង"</item>
+    <item msgid="79513688117503758">"គែម​ខ្លី"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"បញ្ឈរ"</item>
     <item msgid="3199660090246166812">"ផ្ដេក"</item>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index 3950866..61065e0 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"ಪೇಪರ್ ಗಾತ್ರ"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"ಪೇಪರ್ ಗಾತ್ರ:"</string>
     <string name="label_color" msgid="1108690305218188969">"ಬಣ್ಣ"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"ಓರಿಯಂಟೇಶನ್"</string>
     <string name="label_pages" msgid="7768589729282182230">"ಪುಟಗಳು"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"ಎಲ್ಲಾ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"ಕಪ್ಪು &amp; ಬಿಳುಪು"</item>
     <item msgid="2762241247228983754">"ಬಣ್ಣ"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"ಪೋಟ್ರೇಟ್"</item>
     <item msgid="3199660090246166812">"ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್"</item>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index f1a4869..448896c 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"용지 크기"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"용지 크기:"</string>
     <string name="label_color" msgid="1108690305218188969">"색상"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"방향"</string>
     <string name="label_pages" msgid="7768589729282182230">"페이지"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>페이지 모두"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"흑백"</item>
     <item msgid="2762241247228983754">"컬러"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"없음"</item>
+    <item msgid="7296563835355641719">"옆으로 넘김"</item>
+    <item msgid="79513688117503758">"위로 넘김"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"세로"</item>
     <item msgid="3199660090246166812">"가로"</item>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 602f6605..64bd585 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Барактын өлчөмү"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Барактын өлчөмү:"</string>
     <string name="label_color" msgid="1108690305218188969">"Түс"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Багыттоо"</string>
     <string name="label_pages" msgid="7768589729282182230">"Баракчалар"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Бардыгы <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Кара-ак"</item>
     <item msgid="2762241247228983754">"Түстүү"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Тикесинен"</item>
     <item msgid="3199660090246166812">"Туурасынан"</item>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 3a3f6bb..9bd4d83 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"​ຂະ​ໜາດ​ເຈ້ຍ"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"​ຂະ​ໜາດ​ເຈ້ຍ:"</string>
     <string name="label_color" msgid="1108690305218188969">"ສີ"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"ລວງ"</string>
     <string name="label_pages" msgid="7768589729282182230">"ໜ້າ"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"ທັງ​ໝົດ <xliff:g id="PAGE_COUNT">%1$s</xliff:g> ໜ້າ"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"ຂາວດຳ"</item>
     <item msgid="2762241247228983754">"ສີ"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"ບໍ່ມີ"</item>
+    <item msgid="7296563835355641719">"ຂອບຍາວ"</item>
+    <item msgid="79513688117503758">"ຂອບສັ້ນ"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"ລວງຕັ້ງ"</item>
     <item msgid="3199660090246166812">"ລວງນອນ"</item>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 6262a15..1116f6d 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Popieriaus dydis"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Popieriaus dydis:"</string>
     <string name="label_color" msgid="1108690305218188969">"Spalva"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientacija"</string>
     <string name="label_pages" msgid="7768589729282182230">"Puslapiai"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Visi <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Nespalvotas"</item>
     <item msgid="2762241247228983754">"Spalva"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Nėra"</item>
+    <item msgid="7296563835355641719">"Ilgasis kraštas"</item>
+    <item msgid="79513688117503758">"Trumpasis kraštas"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Stačias"</item>
     <item msgid="3199660090246166812">"Gulsčias"</item>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 3a60ee5..d079ea9 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papīra izmērs"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papīra izmērs:"</string>
     <string name="label_color" msgid="1108690305218188969">"Krāsa"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Virziens"</string>
     <string name="label_pages" msgid="7768589729282182230">"Lapas"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Visas <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Melnbalts"</item>
     <item msgid="2762241247228983754">"Krāsa"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Nav"</item>
+    <item msgid="7296563835355641719">"Garā mala"</item>
+    <item msgid="79513688117503758">"Īsā mala"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portrets"</item>
     <item msgid="3199660090246166812">"Ainava"</item>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index 91b5763..fec4841 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Големина на хартија"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Големина на хартија:"</string>
     <string name="label_color" msgid="1108690305218188969">"Боја"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Ориентација"</string>
     <string name="label_pages" msgid="7768589729282182230">"Страници"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Сите <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Црно-бела"</item>
     <item msgid="2762241247228983754">"Во боја"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Портрет"</item>
     <item msgid="3199660090246166812">"Пејзаж"</item>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index a06ca7d..743827f 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"പേപ്പർ വലുപ്പം"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"പേപ്പർ വലുപ്പം:"</string>
     <string name="label_color" msgid="1108690305218188969">"നിറം"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"ഓറിയന്‍റേഷന്‍‌"</string>
     <string name="label_pages" msgid="7768589729282182230">"പേജുകൾ"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"എല്ലാ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"കറുപ്പ് &amp; വെള്ള"</item>
     <item msgid="2762241247228983754">"നിറം"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"പോർട്രെയ്‌റ്റ്"</item>
     <item msgid="3199660090246166812">"ലാൻഡ്‌സ്‌കേപ്പ്"</item>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index 022adda..8d0604a 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Цаасны хэмжээ"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Цаасны хэмжээ:"</string>
     <string name="label_color" msgid="1108690305218188969">"Өнгө"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Чиглэл"</string>
     <string name="label_pages" msgid="7768589729282182230">"Хуудас"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Нийт <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Хар &amp; Цагаан"</item>
     <item msgid="2762241247228983754">"Өнгө"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Хоосон"</item>
+    <item msgid="7296563835355641719">"Урт ирмэг"</item>
+    <item msgid="79513688117503758">"Богино ирмэг"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Босоо"</item>
     <item msgid="3199660090246166812">"Хэвтээ"</item>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index 1fade66..9036e0e 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"कागद आकार"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"कागद आकार:"</string>
     <string name="label_color" msgid="1108690305218188969">"रंग"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"अभिमुखता"</string>
     <string name="label_pages" msgid="7768589729282182230">"पृष्ठे"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"सर्व <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"कृष्‍ण धवल"</item>
     <item msgid="2762241247228983754">"रंग"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"पोट्रेट"</item>
     <item msgid="3199660090246166812">"भूदृश्य"</item>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index a392b76..0c7ecfb 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Saiz kertas"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Saiz kertas:"</string>
     <string name="label_color" msgid="1108690305218188969">"Warna"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
     <string name="label_pages" msgid="7768589729282182230">"Halaman"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Semua <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Hitam &amp; Putih"</item>
     <item msgid="2762241247228983754">"Warna"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Tiada"</item>
+    <item msgid="7296563835355641719">"Sisi panjang"</item>
+    <item msgid="79513688117503758">"Sisi pendek"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Potret"</item>
     <item msgid="3199660090246166812">"Landskap"</item>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index d6eb380..bbf2de4 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"စက္ကူ  ဆိုက်"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"စက္ကူ  ဆိုက်:"</string>
     <string name="label_color" msgid="1108690305218188969">"ရောင်စုံ"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"အနေအထား"</string>
     <string name="label_pages" msgid="7768589729282182230">"စာမျက်နှာများ"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"အားလုံး <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"အဖြူ အမည်း"</item>
     <item msgid="2762241247228983754">"ရောင်စုံ"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"ထောင်လိုက်"</item>
     <item msgid="3199660090246166812">"အလျားလိုက်"</item>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index bf11068..1df90a9 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papirstørrelse"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papirstørrelse:"</string>
     <string name="label_color" msgid="1108690305218188969">"Farge"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Retning"</string>
     <string name="label_pages" msgid="7768589729282182230">"Sider"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Svart og hvitt"</item>
     <item msgid="2762241247228983754">"Farge"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ingen"</item>
+    <item msgid="7296563835355641719">"Langsiden"</item>
+    <item msgid="79513688117503758">"Kortsiden"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Stående"</item>
     <item msgid="3199660090246166812">"Liggende"</item>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index eb97530..c2ecc3b 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"कागजको आकार"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"कागजको आकार:"</string>
     <string name="label_color" msgid="1108690305218188969">"रङ्ग"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"अभिमुखिकरण"</string>
     <string name="label_pages" msgid="7768589729282182230">"पृष्ठहरू"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"सबै <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"कालो &amp; सेतो"</item>
     <item msgid="2762241247228983754">"रङ्ग"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"कुनै पनि होइन"</item>
+    <item msgid="7296563835355641719">"लामो किनारा"</item>
+    <item msgid="79513688117503758">"छोटो किनारा"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"पोट्रेट"</item>
     <item msgid="3199660090246166812">"परिदृश्य"</item>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 5ea52a0..2a6c4c2 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Papierformaat"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Papierformaat:"</string>
     <string name="label_color" msgid="1108690305218188969">"Kleur"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Stand"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pagina\'s"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Zwart-wit"</item>
     <item msgid="2762241247228983754">"Kleur"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Geen"</item>
+    <item msgid="7296563835355641719">"Lange zijde"</item>
+    <item msgid="79513688117503758">"Korte zijde"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portret"</item>
     <item msgid="3199660090246166812">"Landschap"</item>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 609e6e9..a74a890 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Rozmiar papieru"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Rozmiar papieru:"</string>
     <string name="label_color" msgid="1108690305218188969">"Kolor"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientacja"</string>
     <string name="label_pages" msgid="7768589729282182230">"Strony"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Wszystkie <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Czarno-białe"</item>
     <item msgid="2762241247228983754">"Kolor"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Brak"</item>
+    <item msgid="7296563835355641719">"Długa krawędź"</item>
+    <item msgid="79513688117503758">"Krótka krawędź"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Pionowa"</item>
     <item msgid="3199660090246166812">"Pozioma"</item>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 7b47f4c..10b32ca 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Tamanho do papel"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Tamanho do papel:"</string>
     <string name="label_color" msgid="1108690305218188969">"Cor"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
     <string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Todas as <xliff:g id="PAGE_COUNT">%1$s</xliff:g> páginas"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Preto e branco"</item>
     <item msgid="2762241247228983754">"Cor"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Nenhum"</item>
+    <item msgid="7296563835355641719">"Limite grande"</item>
+    <item msgid="79513688117503758">"Limite curto"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Vertical"</item>
     <item msgid="3199660090246166812">"Horizontal"</item>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 3038a7f..eb1d9a0 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Tamanho do papel"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Tamanho do papel:"</string>
     <string name="label_color" msgid="1108690305218188969">"Cor"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
     <string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Todas as <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Preto e branco"</item>
     <item msgid="2762241247228983754">"Cor"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Nenhum"</item>
+    <item msgid="7296563835355641719">"Borda longa"</item>
+    <item msgid="79513688117503758">"Borda curta"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Retrato"</item>
     <item msgid="3199660090246166812">"Paisagem"</item>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 1446a53..15ccb7d 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Formatul hârtiei"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Formatul hârtiei:"</string>
     <string name="label_color" msgid="1108690305218188969">"Color"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientare"</string>
     <string name="label_pages" msgid="7768589729282182230">"Pagini"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Toate cele <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Alb-negru"</item>
     <item msgid="2762241247228983754">"Color"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Fără"</item>
+    <item msgid="7296563835355641719">"Latura lungă"</item>
+    <item msgid="79513688117503758">"Latura scurtă"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portret"</item>
     <item msgid="3199660090246166812">"Peisaj"</item>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index c2a19bb..f2d5bef 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Размер бумаги"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Размер бумаги:"</string>
     <string name="label_color" msgid="1108690305218188969">"Печать"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
     <string name="label_pages" msgid="7768589729282182230">"Страницы"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Все <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Черно-белая"</item>
     <item msgid="2762241247228983754">"Цветная"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Нет"</item>
+    <item msgid="7296563835355641719">"Длинный край"</item>
+    <item msgid="79513688117503758">"Короткий край"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Книга"</item>
     <item msgid="3199660090246166812">"Альбом"</item>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 386ce8d..f1b40c6 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"කඩදාසියේ ප්‍රමාණය"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"කඩදාසියේ ප්‍රමාණය:"</string>
     <string name="label_color" msgid="1108690305218188969">"වර්ණය"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"දිශානතිය"</string>
     <string name="label_pages" msgid="7768589729282182230">"පිටු"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"සියලුම <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"කළු සහ සුදු"</item>
     <item msgid="2762241247228983754">"වර්ණය"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"කිසිවක් නැත"</item>
+    <item msgid="7296563835355641719">"දිගු දාරය"</item>
+    <item msgid="79513688117503758">"කෙටි දාරය"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"ප්‍රතිමුර්ති"</item>
     <item msgid="3199660090246166812">"තිරස් දර්ශනය"</item>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 5be2034..fbf2bc7 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Veľkosť papiera"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Veľkosť papiera:"</string>
     <string name="label_color" msgid="1108690305218188969">"Farba"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientácia"</string>
     <string name="label_pages" msgid="7768589729282182230">"Strany"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Všetky: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Čiernobiele"</item>
     <item msgid="2762241247228983754">"Farba"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Žiadne"</item>
+    <item msgid="7296563835355641719">"Dlhý okraj"</item>
+    <item msgid="79513688117503758">"Krátky okraj"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Na výšku"</item>
     <item msgid="3199660090246166812">"Na šírku"</item>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index ee15103..a441d7c 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Velikost papirja"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Velikost papirja:"</string>
     <string name="label_color" msgid="1108690305218188969">"Barvno"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Postavitev"</string>
     <string name="label_pages" msgid="7768589729282182230">"Strani"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Vse (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Črno-belo"</item>
     <item msgid="2762241247228983754">"Barvno"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Brez"</item>
+    <item msgid="7296563835355641719">"Dolgi rob"</item>
+    <item msgid="79513688117503758">"Kratki rob"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Pokončno"</item>
     <item msgid="3199660090246166812">"Ležeče"</item>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 6ca94aa..622c84b 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Величина папира"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Величина папира:"</string>
     <string name="label_color" msgid="1108690305218188969">"Боја"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Положај"</string>
     <string name="label_pages" msgid="7768589729282182230">"Странице"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Све странице (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Црно-бело"</item>
     <item msgid="2762241247228983754">"Боја"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ништа"</item>
+    <item msgid="7296563835355641719">"Дуга ивица"</item>
+    <item msgid="79513688117503758">"Кратка ивица"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Усправно"</item>
     <item msgid="3199660090246166812">"Водоравно"</item>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 4c439be..09ce6ee 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Pappersstorlek"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Pappersstorlek:"</string>
     <string name="label_color" msgid="1108690305218188969">"Färg"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Orientering"</string>
     <string name="label_pages" msgid="7768589729282182230">"Sidor"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Alla <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Svartvit"</item>
     <item msgid="2762241247228983754">"Färg"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Ingen"</item>
+    <item msgid="7296563835355641719">"Långsidan"</item>
+    <item msgid="79513688117503758">"Kortsidan"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Stående"</item>
     <item msgid="3199660090246166812">"Liggande"</item>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index e454704..a0497e6 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Ukubwa wa karatasi"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Ukubwa wa karatasi:"</string>
     <string name="label_color" msgid="1108690305218188969">"Rangi"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Mkao"</string>
     <string name="label_pages" msgid="7768589729282182230">"Kurasa"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Kurasa zote <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Nyeusi na Nyeupe"</item>
     <item msgid="2762241247228983754">"Rangi"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Hamna"</item>
+    <item msgid="7296563835355641719">"Ukingo mrefu"</item>
+    <item msgid="79513688117503758">"Ukingo mfupi"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Wima"</item>
     <item msgid="3199660090246166812">"Mlalo"</item>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index 0421bd6..a50470a 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"காகித அளவு"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"காகித அளவு:"</string>
     <string name="label_color" msgid="1108690305218188969">"வண்ணம்"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"திசையமைப்பு"</string>
     <string name="label_pages" msgid="7768589729282182230">"பக்கங்கள்"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"எல்லாம்: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"கருப்பு &amp; வெள்ளை"</item>
     <item msgid="2762241247228983754">"வண்ணம்"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"உறுவப்படம்"</item>
     <item msgid="3199660090246166812">"நிலத்தோற்றம்"</item>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index edb6e60..b35520d 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"కాగితపు పరిమాణం"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"కాగితపు పరిమాణం:"</string>
     <string name="label_color" msgid="1108690305218188969">"రంగు"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"దృగ్విన్యాసం"</string>
     <string name="label_pages" msgid="7768589729282182230">"పేజీలు"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"మొత్తం <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"నలుపు &amp; తెలుపు"</item>
     <item msgid="2762241247228983754">"రంగు"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"పోర్ట్రెయిట్"</item>
     <item msgid="3199660090246166812">"ల్యాండ్‌స్కేప్"</item>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index cfffa0b..9c8d55c 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"ขนาดของกระดาษ"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"ขนาดของกระดาษ:"</string>
     <string name="label_color" msgid="1108690305218188969">"สี"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"การวางแนว"</string>
     <string name="label_pages" msgid="7768589729282182230">"หน้า"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"ทั้ง <xliff:g id="PAGE_COUNT">%1$s</xliff:g> หน้า"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"ขาวดำ"</item>
     <item msgid="2762241247228983754">"สี"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"ไม่มี"</item>
+    <item msgid="7296563835355641719">"ขอบยาว"</item>
+    <item msgid="79513688117503758">"ขอบสั้น"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"แนวตั้ง"</item>
     <item msgid="3199660090246166812">"แนวนอน"</item>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index dfb0450..9e4c7be 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Laki ng papel"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Laki ng papel:"</string>
     <string name="label_color" msgid="1108690305218188969">"Kulay"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Oryentasyon"</string>
     <string name="label_pages" msgid="7768589729282182230">"Mga Page"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Lahat ng <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Black &amp; White"</item>
     <item msgid="2762241247228983754">"Kulay"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Wala"</item>
+    <item msgid="7296563835355641719">"Long edge"</item>
+    <item msgid="79513688117503758">"Short edge"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Portrait"</item>
     <item msgid="3199660090246166812">"Landscape"</item>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 50befba..902f4ce 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Kağıt boyutu"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Kağıt boyutu:"</string>
     <string name="label_color" msgid="1108690305218188969">"Renkli"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Sayfa yönü"</string>
     <string name="label_pages" msgid="7768589729282182230">"Sayfa"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> sayfanın tamamı"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Siyah Beyaz"</item>
     <item msgid="2762241247228983754">"Renkli"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Yok"</item>
+    <item msgid="7296563835355641719">"Uzun kenar"</item>
+    <item msgid="79513688117503758">"Kısa kenar"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Dikey"</item>
     <item msgid="3199660090246166812">"Yatay"</item>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index 8a924e6..71f9d61 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Розмір паперу"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Розмір паперу:"</string>
     <string name="label_color" msgid="1108690305218188969">"Колір"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Орієнтація"</string>
     <string name="label_pages" msgid="7768589729282182230">"Сторінки"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Усі <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Чорно-білий"</item>
     <item msgid="2762241247228983754">"Колір"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Немає"</item>
+    <item msgid="7296563835355641719">"Довгий край"</item>
+    <item msgid="79513688117503758">"Короткий край"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Книжкова"</item>
     <item msgid="3199660090246166812">"Альбомна"</item>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index 722d027..2b32c30 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"کاغذ کا سائز"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"کاغذ کا سائز:"</string>
     <string name="label_color" msgid="1108690305218188969">"رنگ"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"سمت بندی"</string>
     <string name="label_pages" msgid="7768589729282182230">"صفحات"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"سبھی <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"سیاہ و سفید"</item>
     <item msgid="2762241247228983754">"رنگ"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"پورٹریٹ"</item>
     <item msgid="3199660090246166812">"لینڈ اسکیپ"</item>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index f62728f..57103d4 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Qog‘oz o‘lchami"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Qog‘oz o‘lchami:"</string>
     <string name="label_color" msgid="1108690305218188969">"Rang"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Joylashuv"</string>
     <string name="label_pages" msgid="7768589729282182230">"Sahifalar"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Barchasi (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -77,6 +79,9 @@
     <item msgid="7602948745415174937">"Oq &amp; qora"</item>
     <item msgid="2762241247228983754">"Rang"</item>
   </string-array>
+    <!-- no translation found for duplex_mode_labels:0 (3882302912790928315) -->
+    <!-- no translation found for duplex_mode_labels:1 (7296563835355641719) -->
+    <!-- no translation found for duplex_mode_labels:2 (79513688117503758) -->
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Bo‘yiga"</item>
     <item msgid="3199660090246166812">"Eniga"</item>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index fa0d26e..3a9f8d4 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Khổ giấy"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Khổ giấy:"</string>
     <string name="label_color" msgid="1108690305218188969">"Màu"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Hướng"</string>
     <string name="label_pages" msgid="7768589729282182230">"Trang"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Tất cả <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Đen trắng"</item>
     <item msgid="2762241247228983754">"Màu"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Không có"</item>
+    <item msgid="7296563835355641719">"Cạnh dài"</item>
+    <item msgid="79513688117503758">"Cạnh ngắn"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Dọc"</item>
     <item msgid="3199660090246166812">"Ngang"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 77ecb21..80bab12 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"纸张尺寸"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"纸张尺寸:"</string>
     <string name="label_color" msgid="1108690305218188969">"颜色"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"方向"</string>
     <string name="label_pages" msgid="7768589729282182230">"页数"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"全部<xliff:g id="PAGE_COUNT">%1$s</xliff:g>页"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"黑白"</item>
     <item msgid="2762241247228983754">"彩色"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"无"</item>
+    <item msgid="7296563835355641719">"长边"</item>
+    <item msgid="79513688117503758">"短边"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"纵向"</item>
     <item msgid="3199660090246166812">"横向"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index d2fa629..bf6262c 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"紙張大小"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"紙張大小:"</string>
     <string name="label_color" msgid="1108690305218188969">"顏色"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"方向"</string>
     <string name="label_pages" msgid="7768589729282182230">"頁數"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"全部 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"黑白"</item>
     <item msgid="2762241247228983754">"彩色"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"無"</item>
+    <item msgid="7296563835355641719">"長邊"</item>
+    <item msgid="79513688117503758">"短邊"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"直向"</item>
     <item msgid="3199660090246166812">"橫向"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index 3e26a5e..d822b41 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"紙張大小"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"紙張大小:"</string>
     <string name="label_color" msgid="1108690305218188969">"色彩"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"方向"</string>
     <string name="label_pages" msgid="7768589729282182230">"頁面"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"全部 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"黑白"</item>
     <item msgid="2762241247228983754">"彩色"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"無"</item>
+    <item msgid="7296563835355641719">"長邊"</item>
+    <item msgid="79513688117503758">"短邊"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"縱向"</item>
     <item msgid="3199660090246166812">"橫向"</item>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index f8a27bc..2b05cb0 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -24,6 +24,8 @@
     <string name="label_paper_size" msgid="908654383827777759">"Usayizi wekhasi"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Usayizi wekhasi"</string>
     <string name="label_color" msgid="1108690305218188969">"Umbala"</string>
+    <!-- no translation found for label_duplex (1263181386446435253) -->
+    <skip />
     <string name="label_orientation" msgid="2853142581990496477">"Umumo"</string>
     <string name="label_pages" msgid="7768589729282182230">"Amakhasi"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"Konke <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
@@ -77,6 +79,11 @@
     <item msgid="7602948745415174937">"Okumnyama nokumhlophe"</item>
     <item msgid="2762241247228983754">"Umbala"</item>
   </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"Lutho"</item>
+    <item msgid="7296563835355641719">"Emaphethelweni amade"</item>
+    <item msgid="79513688117503758">"Emaphethelweni amafushane"</item>
+  </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Ukuma ngobude"</item>
     <item msgid="3199660090246166812">"Ukwakheka kwezwe"</item>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index ab633ea..9d67ccc 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -40,6 +40,9 @@
     <!-- Label of the color mode widget. [CHAR LIMIT=20] -->
     <string name="label_color">Color</string>
 
+    <!-- Label of the duplex mode widget. [CHAR LIMIT=20] -->
+    <string name="label_duplex">Duplex</string>
+
     <!-- Label of the orientation widget. [CHAR LIMIT=20] -->
     <string name="label_orientation">Orientation</string>
 
@@ -188,12 +191,22 @@
 
     <!-- Color mode labels. -->
     <string-array name="color_mode_labels">
-        <!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
+        <!-- Color mode label: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
         <item>Black &amp; White</item>
         <!-- Color mode label: Color color scheme, e.g. many colors are used. [CHAR LIMIT=20] -->
         <item>Color</item>
     </string-array>
 
+    <!-- Duplex mode labels. -->
+    <string-array name="duplex_mode_labels">
+        <!-- Duplex mode label: No duplex supported. [CHAR LIMIT=20] -->
+        <item>None</item>
+        <!-- Duplex mode label: Turn page sideways along the long edge like a book. [CHAR LIMIT=20] -->
+        <item>Long edge</item>
+        <!-- Duplex mode label: Turn page upwards along the short edge like a notepad. [CHAR LIMIT=20] -->
+        <item>Short edge</item>
+    </string-array>
+
     <!-- Orientation labels. -->
     <string-array name="orientation_labels">
         <!-- Orientation label: Portrait page orientation. [CHAR LIMIT=30] -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 2cc5e04..377d2d5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -695,6 +695,7 @@
         private static final String TAG_MARGINS = "margins";
 
         private static final String ATTR_COLOR_MODE = "colorMode";
+        private static final String ATTR_DUPLEX_MODE = "duplexMode";
 
         private static final String ATTR_LOCAL_ID = "localId";
         private static final String ATTR_SERVICE_NAME = "serviceName";
@@ -823,6 +824,10 @@
                         serializer.attribute(null, ATTR_COLOR_MODE,
                                 String.valueOf(colorMode));
 
+                        final int duplexMode = attributes.getDuplexMode();
+                        serializer.attribute(null, ATTR_DUPLEX_MODE,
+                                String.valueOf(duplexMode));
+
                         MediaSize mediaSize = attributes.getMediaSize();
                         if (mediaSize != null) {
                             serializer.startTag(null, TAG_MEDIA_SIZE);
@@ -1057,6 +1062,12 @@
                 String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
                 builder.setColorMode(Integer.parseInt(colorMode));
 
+                String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
+                // Duplex mode was added later, so null check is needed.
+                if (duplexMode != null) {
+                    builder.setDuplexMode(Integer.parseInt(duplexMode));
+                }
+
                 parser.next();
 
                 skipEmptyTextTags(parser);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index f3a5c95..4ba04e5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -184,6 +184,9 @@
     private Spinner mColorModeSpinner;
     private ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
 
+    private Spinner mDuplexModeSpinner;
+    private ArrayAdapter<SpinnerItem<Integer>> mDuplexModeSpinnerAdapter;
+
     private Spinner mOrientationSpinner;
     private ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
 
@@ -767,6 +770,21 @@
                     }
                 }
             }
+
+            // Take the duplex mode only if the current printer supports it.
+            final int currDuplexMode = currAttributes.getDuplexMode();
+            final int newDuplexMode = newAttributes.getDuplexMode();
+            if (currDuplexMode != newDuplexMode) {
+                final int duplexModeCount = mDuplexModeSpinner.getCount();
+                for (int i = 0; i < duplexModeCount; i++) {
+                    final int supportedDuplexMode = mDuplexModeSpinnerAdapter.getItem(i).value;
+                    if (supportedDuplexMode == newDuplexMode) {
+                        currAttributes.setDuplexMode(newDuplexMode);
+                        mDuplexModeSpinner.setSelection(i);
+                        break;
+                    }
+                }
+            }
         }
 
         // Handle selected page changes making sure they are in the doc.
@@ -985,6 +1003,12 @@
             attributes.setColorMode(defaults.getColorMode());
         }
 
+        // Duplex mode.
+        final int duplexMode = attributes.getDuplexMode();
+        if ((capabilities.getDuplexModes() & duplexMode) == 0) {
+            attributes.setDuplexMode(defaults.getDuplexMode());
+        }
+
         // Resolution
         Resolution resolution = attributes.getResolution();
         if (resolution == null || !capabilities.getResolutions().contains(resolution)) {
@@ -1111,6 +1135,13 @@
         mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
         mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
 
+        // Duplex mode.
+        mDuplexModeSpinnerAdapter = new ArrayAdapter<>(
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
+        mDuplexModeSpinner = (Spinner) findViewById(R.id.duplex_spinner);
+        mDuplexModeSpinner.setAdapter(mDuplexModeSpinnerAdapter);
+        mDuplexModeSpinner.setOnItemSelectedListener(itemSelectedListener);
+
         // Orientation
         mOrientationSpinnerAdapter = new ArrayAdapter<>(
                 this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
@@ -1187,6 +1218,7 @@
             mCopiesEditText.setFocusable(false);
             mMediaSizeSpinner.setEnabled(false);
             mColorModeSpinner.setEnabled(false);
+            mDuplexModeSpinner.setEnabled(false);
             mOrientationSpinner.setEnabled(false);
             mRangeOptionsSpinner.setEnabled(false);
             mPageRangeEditText.setEnabled(false);
@@ -1202,6 +1234,7 @@
             mCopiesEditText.setFocusable(false);
             mMediaSizeSpinner.setEnabled(false);
             mColorModeSpinner.setEnabled(false);
+            mDuplexModeSpinner.setEnabled(false);
             mOrientationSpinner.setEnabled(false);
             mRangeOptionsSpinner.setEnabled(false);
             mPageRangeEditText.setEnabled(false);
@@ -1317,7 +1350,7 @@
                 final int colorMode = 1 << colorBitOffset;
                 if (colorMode == oldColorMode) {
                     // Update the index of the old selection.
-                    oldColorModeNewIndex = colorBitOffset;
+                    oldColorModeNewIndex = mColorModeSpinnerAdapter.getCount();
                 }
                 remainingColorModes &= ~colorMode;
                 mColorModeSpinnerAdapter.add(new SpinnerItem<>(colorMode,
@@ -1339,11 +1372,81 @@
                             mColorModeSpinner.setSelection(i);
                         }
                         attributes.setColorMode(selectedColorMode);
+                        break;
                     }
                 }
             }
         }
 
+        // Duplex mode.
+        mDuplexModeSpinner.setEnabled(true);
+        final int duplexModes = capabilities.getDuplexModes();
+
+        // If the duplex modes changed, we update the adapter and the spinner.
+        // Note that we use bit count +1 to account for the no duplex option.
+        boolean duplexModesChanged = false;
+        if (Integer.bitCount(duplexModes) != mDuplexModeSpinnerAdapter.getCount()) {
+            duplexModesChanged = true;
+        } else {
+            int remainingDuplexModes = duplexModes;
+            int adapterIndex = 0;
+            while (remainingDuplexModes != 0) {
+                final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
+                final int duplexMode = 1 << duplexBitOffset;
+                remainingDuplexModes &= ~duplexMode;
+                if (duplexMode != mDuplexModeSpinnerAdapter.getItem(adapterIndex).value) {
+                    duplexModesChanged = true;
+                    break;
+                }
+                adapterIndex++;
+            }
+        }
+        if (duplexModesChanged) {
+            // Remember the old duplex mode to try selecting it again. Also the fallback
+            // is no duplexing which is always the first item in the dropdown.
+            int oldDuplexModeNewIndex = AdapterView.INVALID_POSITION;
+            final int oldDuplexMode = attributes.getDuplexMode();
+
+            // Rebuild the adapter data.
+            mDuplexModeSpinnerAdapter.clear();
+            String[] duplexModeLabels = getResources().getStringArray(R.array.duplex_mode_labels);
+            int remainingDuplexModes = duplexModes;
+            while (remainingDuplexModes != 0) {
+                final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
+                final int duplexMode = 1 << duplexBitOffset;
+                if (duplexMode == oldDuplexMode) {
+                    // Update the index of the old selection.
+                    oldDuplexModeNewIndex = mDuplexModeSpinnerAdapter.getCount();
+                }
+                remainingDuplexModes &= ~duplexMode;
+                mDuplexModeSpinnerAdapter.add(new SpinnerItem<>(duplexMode,
+                        duplexModeLabels[duplexBitOffset]));
+            }
+
+            if (oldDuplexModeNewIndex != AdapterView.INVALID_POSITION) {
+                // Select the old duplex mode - nothing really changed.
+                if (mDuplexModeSpinner.getSelectedItemPosition() != oldDuplexModeNewIndex) {
+                    mDuplexModeSpinner.setSelection(oldDuplexModeNewIndex);
+                }
+            } else {
+                // Select the default.
+                final int selectedDuplexMode = defaultAttributes.getDuplexMode();
+                final int itemCount = mDuplexModeSpinnerAdapter.getCount();
+                for (int i = 0; i < itemCount; i++) {
+                    SpinnerItem<Integer> item = mDuplexModeSpinnerAdapter.getItem(i);
+                    if (selectedDuplexMode == item.value) {
+                        if (mDuplexModeSpinner.getSelectedItemPosition() != i) {
+                            mDuplexModeSpinner.setSelection(i);
+                        }
+                        attributes.setDuplexMode(selectedDuplexMode);
+                        break;
+                    }
+                }
+            }
+        }
+
+        mDuplexModeSpinner.setEnabled(mDuplexModeSpinnerAdapter.getCount() > 1);
+
         // Orientation
         mOrientationSpinner.setEnabled(true);
         MediaSize mediaSize = attributes.getMediaSize();
@@ -2173,6 +2276,9 @@
             } else if (spinner == mColorModeSpinner) {
                 SpinnerItem<Integer> colorModeItem = mColorModeSpinnerAdapter.getItem(position);
                 mPrintJob.getAttributes().setColorMode(colorModeItem.value);
+            } else if (spinner == mDuplexModeSpinner) {
+                SpinnerItem<Integer> duplexModeItem = mDuplexModeSpinnerAdapter.getItem(position);
+                mPrintJob.getAttributes().setDuplexMode(duplexModeItem.value);
             } else if (spinner == mOrientationSpinner) {
                 SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position);
                 PrintAttributes attributes = mPrintJob.getAttributes();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
index feb0316..6a6f1d3 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
@@ -48,16 +48,19 @@
     }
 
     @Override
+    public CharSequence getAccessibilityClassName() {
+        return CompoundButton.class.getName();
+    }
+
+    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
-        event.setClassName(CompoundButton.class.getName());
         event.setChecked(isSelected());
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(CompoundButton.class.getName());
         info.setSelected(false);
         info.setCheckable(true);
         info.setChecked(isSelected());
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
new file mode 100644
index 0000000..0245ed3
--- /dev/null
+++ b/packages/SettingsLib/Android.mk
@@ -0,0 +1,9 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := SettingsLib
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core/res/res/color/btn_material_accent.xml b/packages/SettingsLib/AndroidManifest.xml
similarity index 64%
copy from core/res/res/color/btn_material_accent.xml
copy to packages/SettingsLib/AndroidManifest.xml
index 71469b6..eacafd5 100644
--- a/core/res/res/color/btn_material_accent.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+     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.
@@ -14,9 +15,6 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/disabledAlpha"
-          android:color="@color/button_material_dark" />
-    <item android:color="?attr/colorAccent" />
-</selector>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.settingslib">
+</manifest>
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
new file mode 100644
index 0000000..b017047
--- /dev/null
+++ b/packages/SettingsLib/common.mk
@@ -0,0 +1,18 @@
+#
+# Include this make file to build your application against this module.
+#
+# Make sure to include it after you've set all your desired LOCAL variables.
+# Note that you must explicitly set your LOCAL_RESOURCE_DIR before including
+# this file.
+#
+# For example:
+#
+#   LOCAL_RESOURCE_DIR := \
+#        $(LOCAL_PATH)/res
+#
+#   include frameworks/base/packages/SettingsLib/common.mk
+#
+
+LOCAL_RESOURCE_DIR += $(call my-dir)/res
+LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.settingslib
+LOCAL_STATIC_JAVA_LIBRARIES += SettingsLib
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
new file mode 100644
index 0000000..a52ed69
--- /dev/null
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Wi-Fi settings -->
+
+    <!-- Match this with the order of NetworkInfo.DetailedState. --> <skip />
+    <!-- Wi-Fi settings. The status messages when the network is unknown. -->
+    <string-array name="wifi_status">
+        <!-- Status message of Wi-Fi when it is idle. -->
+        <item></item>
+        <!-- Status message of Wi-Fi when it is scanning. -->
+        <item>Scanning\u2026</item>
+        <!-- Status message of Wi-Fi when it is connecting. -->
+        <item>Connecting\u2026</item>
+        <!-- Status message of Wi-Fi when it is authenticating. -->
+        <item>Authenticating\u2026</item>
+        <!-- Status message of Wi-Fi when it is obtaining IP address. -->
+        <item>Obtaining IP address\u2026</item>
+        <!-- Status message of Wi-Fi when it is connected. -->
+        <item>Connected</item>
+        <!-- Status message of Wi-Fi when it is suspended. -->
+        <item>Suspended</item>
+        <!-- Status message of Wi-Fi when it is disconnecting. -->
+        <item>Disconnecting\u2026</item>
+        <!-- Status message of Wi-Fi when it is disconnected. -->
+        <item>Disconnected</item>
+        <!-- Status message of Wi-Fi when it is a failure. -->
+        <item>Unsuccessful</item>
+        <!-- Status message of Wi-Fi when it is blocked. -->
+        <item>Blocked</item>
+        <!-- Status message of Wi-Fi when connectiong is being verified. -->
+        <item>Temporarily avoiding poor connection</item>
+    </string-array>
+
+    <!-- Match this with the order of NetworkInfo.DetailedState. --> <skip />
+    <!-- Wi-Fi settings. The status messages when the network is known. -->
+    <string-array name="wifi_status_with_ssid">
+        <!-- Status message of Wi-Fi when it is idle. -->
+        <item></item>
+        <!-- Status message of Wi-Fi when it is scanning. -->
+        <item>Scanning\u2026</item>
+        <!-- Status message of Wi-Fi when it is connecting to a network. -->
+        <item>Connecting to <xliff:g id="network_name">%1$s</xliff:g>\u2026</item>
+        <!-- Status message of Wi-Fi when it is authenticating with a network. -->
+        <item>Authenticating with <xliff:g id="network_name">%1$s</xliff:g>\u2026</item>
+        <!-- Status message of Wi-Fi when it is obtaining IP address from a network. -->
+        <item>Obtaining IP address from <xliff:g id="network_name">%1$s</xliff:g>\u2026</item>
+        <!-- Status message of Wi-Fi when it is connected to a network. -->
+        <item>Connected to <xliff:g id="network_name">%1$s</xliff:g></item>
+        <!-- Status message of Wi-Fi when it is suspended. -->
+        <item>Suspended</item>
+        <!-- Status message of Wi-Fi when it is disconnecting from a network. -->
+        <item>Disconnecting from <xliff:g id="network_name">%1$s</xliff:g>\u2026</item>
+        <!-- Status message of Wi-Fi when it is disconnected. -->
+        <item>Disconnected</item>
+        <!-- Status message of Wi-Fi when it is a failure. -->
+        <item>Unsuccessful</item>
+        <!-- Status message of Wi-Fi when it is blocked. -->
+        <item>Blocked</item>
+        <!-- Status message of Wi-Fi when connectiong is being verified. -->
+        <item>Temporarily avoiding poor connection</item>
+    </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
new file mode 100644
index 0000000..f055a2c
--- /dev/null
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Toast message when Wi-Fi cannot scan for networks -->
+    <string name="wifi_fail_to_scan">Can\'t scan for networks</string>
+    <!-- Do not translate.  Concise terminology for wifi with WEP security -->
+    <string name="wifi_security_short_wep">WEP</string>
+    <!-- Do not translate.  Concise terminology for wifi with WPA security -->
+    <string name="wifi_security_short_wpa">WPA</string>
+    <!-- Do not translate.  Concise terminology for wifi with WPA2 security -->
+    <string name="wifi_security_short_wpa2">WPA2</string>
+    <!-- Do not translate.  Concise terminology for wifi with both WPA/WPA2 security -->
+    <string name="wifi_security_short_wpa_wpa2">WPA/WPA2</string>
+    <!-- Do not translate.  Concise terminology for wifi with unknown PSK type -->
+    <string name="wifi_security_short_psk_generic">@string/wifi_security_short_wpa_wpa2</string>
+    <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP security -->
+    <string name="wifi_security_short_eap">802.1x</string>
+
+    <!-- Used in Wi-Fi settings dialogs when Wi-Fi does not have any security. -->
+    <string name="wifi_security_none">None</string>
+
+    <!-- Do not translate.  Terminology for wifi with WEP security -->
+    <string name="wifi_security_wep">WEP</string>
+    <!-- Do not translate.  Terminology for wifi with WPA security -->
+    <string name="wifi_security_wpa">WPA PSK</string>
+    <!-- Do not translate.  Terminology for wifi with WPA2 security -->
+    <string name="wifi_security_wpa2">WPA2 PSK</string>
+    <!-- Do not translate.  Terminology for wifi with both WPA/WPA2 security, or unknown -->
+    <string name="wifi_security_wpa_wpa2">WPA/WPA2 PSK</string>
+    <!-- Do not translate.  Terminology for wifi with unknown PSK type -->
+    <string name="wifi_security_psk_generic">@string/wifi_security_wpa_wpa2</string>
+    <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP security -->
+    <string name="wifi_security_eap">802.1x EAP</string>
+
+    <!-- Summary for the remembered network. -->
+    <string name="wifi_remembered">Saved</string>
+    <!-- Status for networks disabled for unknown reason -->
+    <string name="wifi_disabled_generic">Disabled</string>
+    <!-- Status for networked disabled from a DNS or DHCP failure -->
+    <string name="wifi_disabled_network_failure">IP Configuration Failure</string>
+    <!-- Status for networked disabled from a wifi association failure -->
+    <string name="wifi_disabled_wifi_failure">WiFi Connection Failure</string>
+    <!-- Status for networks disabled from authentication failure (wrong password
+         or certificate). -->
+    <string name="wifi_disabled_password_failure">Authentication problem</string>
+    <!-- Summary for the remembered network but currently not in range. -->
+    <string name="wifi_not_in_range">Not in range</string>
+    <!-- Summary for the remembered network but no internet connection was detected. -->
+    <string name="wifi_no_internet">No Internet Access Detected, won\'t automatically reconnect.</string>
+
+    <!-- Status message of Wi-Fi when it is connected by a Wi-Fi assistant application. [CHAR LIMIT=NONE] -->
+    <string name="connected_via_wfa">Connected via Wi\u2011Fi assistant</string>
+</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/WirelessUtils.java b/packages/SettingsLib/src/com/android/settingslib/WirelessUtils.java
new file mode 100644
index 0000000..0346a77
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/WirelessUtils.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib;
+
+import android.content.Context;
+import android.provider.Settings;
+
+public class WirelessUtils {
+
+    public static boolean isRadioAllowed(Context context, String type) {
+        if (!isAirplaneModeOn(context)) {
+            return true;
+        }
+        String toggleable = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+        return toggleable != null && toggleable.contains(type);
+    }
+
+    public static boolean isAirplaneModeOn(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
new file mode 100644
index 0000000..e8ab220
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.wifi;
+
+import android.content.Context;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.settingslib.R;
+
+import java.util.Map;
+
+
+public class AccessPoint implements Comparable<AccessPoint> {
+    static final String TAG = "SettingsLib.AccessPoint";
+
+    /**
+     * Lower bound on the 2.4 GHz (802.11b/g/n) WLAN channels
+     */
+    public static final int LOWER_FREQ_24GHZ = 2400;
+
+    /**
+     * Upper bound on the 2.4 GHz (802.11b/g/n) WLAN channels
+     */
+    public static final int HIGHER_FREQ_24GHZ = 2500;
+
+    /**
+     * Lower bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
+     */
+    public static final int LOWER_FREQ_5GHZ = 4900;
+
+    /**
+     * Upper bound on the 5.0 GHz (802.11a/h/j/n/ac) WLAN channels
+     */
+    public static final int HIGHER_FREQ_5GHZ = 5900;
+
+
+    /**
+     * Experimental: we should be able to show the user the list of BSSIDs and bands
+     *  for that SSID.
+     *  For now this data is used only with Verbose Logging so as to show the band and number
+     *  of BSSIDs on which that network is seen.
+     */
+    public LruCache<String, ScanResult> mScanResultCache;
+    private static final String KEY_NETWORKINFO = "key_networkinfo";
+    private static final String KEY_WIFIINFO = "key_wifiinfo";
+    private static final String KEY_SCANRESULT = "key_scanresult";
+    private static final String KEY_CONFIG = "key_config";
+
+    /**
+     * These values are matched in string arrays -- changes must be kept in sync
+     */
+    public static final int SECURITY_NONE = 0;
+    public static final int SECURITY_WEP = 1;
+    public static final int SECURITY_PSK = 2;
+    public static final int SECURITY_EAP = 3;
+
+    private static final int PSK_UNKNOWN = 0;
+    private static final int PSK_WPA = 1;
+    private static final int PSK_WPA2 = 2;
+    private static final int PSK_WPA_WPA2 = 3;
+
+    private static final int VISIBILITY_OUTDATED_AGE_IN_MILLI = 20000;
+    private final Context mContext;
+
+    private String ssid;
+    private int security;
+    private int networkId = WifiConfiguration.INVALID_NETWORK_ID;
+
+    private int pskType = PSK_UNKNOWN;
+
+    private WifiConfiguration mConfig;
+    private ScanResult mScanResult;
+
+    private int mRssi = Integer.MAX_VALUE;
+    private long mSeen = 0;
+
+    private WifiInfo mInfo;
+    private NetworkInfo mNetworkInfo;
+    private AccessPointListener mAccessPointListener;
+
+    private Object mTag;
+
+    public AccessPoint(Context context, Bundle savedState) {
+        mContext = context;
+        mConfig = savedState.getParcelable(KEY_CONFIG);
+        if (mConfig != null) {
+            loadConfig(mConfig);
+        }
+        mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT);
+        if (mScanResult != null) {
+            loadResult(mScanResult);
+        }
+        mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);
+        if (savedState.containsKey(KEY_NETWORKINFO)) {
+            mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO);
+        }
+        update(mInfo, mNetworkInfo);
+    }
+
+    AccessPoint(Context context, ScanResult result) {
+        mContext = context;
+        loadResult(result);
+    }
+
+    AccessPoint(Context context, WifiConfiguration config) {
+        mContext = context;
+        loadConfig(config);
+    }
+
+    @Override
+    public int compareTo(AccessPoint other) {
+        // Active one goes first.
+        if (isActive() && !other.isActive()) return -1;
+        if (!isActive() && other.isActive()) return 1;
+
+        // Reachable one goes before unreachable one.
+        if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1;
+        if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1;
+
+        // Configured one goes before unconfigured one.
+        if (networkId != WifiConfiguration.INVALID_NETWORK_ID
+                && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1;
+        if (networkId == WifiConfiguration.INVALID_NETWORK_ID
+                && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1;
+
+        // Sort by signal strength.
+        int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi);
+        if (difference != 0) {
+            return difference;
+        }
+        // Sort by ssid.
+        return ssid.compareToIgnoreCase(other.ssid);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof AccessPoint)) return false;
+        return (this.compareTo((AccessPoint) other) == 0);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 0;
+        if (mInfo != null) result += 13 * mInfo.hashCode();
+        result += 19 * mRssi;
+        result += 23 * networkId;
+        result += 29 * ssid.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder().append("AccessPoint(")
+                .append(ssid);
+        if (isSaved()) {
+            builder.append(',').append("saved");
+        }
+        if (isActive()) {
+            builder.append(',').append("active");
+        }
+        if (isEphemeral()) {
+            builder.append(',').append("ephemeral");
+        }
+        if (isConnectable()) {
+            builder.append(',').append("connectable");
+        }
+        if (security != SECURITY_NONE) {
+            builder.append(',').append(securityToString(security, pskType));
+        }
+        return builder.append(')').toString();
+    }
+
+    public boolean matches(ScanResult result) {
+        return ssid.equals(result.SSID) && security == getSecurity(result);
+    }
+
+    public boolean matches(WifiConfiguration config) {
+        return ssid.equals(removeDoubleQuotes(config.SSID)) && security == getSecurity(config);
+    }
+
+    public WifiConfiguration getConfig() {
+        return mConfig;
+    }
+
+    public void clearConfig() {
+        mConfig = null;
+        networkId = WifiConfiguration.INVALID_NETWORK_ID;
+    }
+
+    public WifiInfo getInfo() {
+        return mInfo;
+    }
+
+    public int getLevel() {
+        if (mRssi == Integer.MAX_VALUE) {
+            return -1;
+        }
+        return WifiManager.calculateSignalLevel(mRssi, 4);
+    }
+
+    public NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
+    }
+
+    public int getSecurity() {
+        return security;
+    }
+
+    public String getSecurityString(boolean concise) {
+        Context context = mContext;
+        switch(security) {
+            case SECURITY_EAP:
+                return concise ? context.getString(R.string.wifi_security_short_eap) :
+                    context.getString(R.string.wifi_security_eap);
+            case SECURITY_PSK:
+                switch (pskType) {
+                    case PSK_WPA:
+                        return concise ? context.getString(R.string.wifi_security_short_wpa) :
+                            context.getString(R.string.wifi_security_wpa);
+                    case PSK_WPA2:
+                        return concise ? context.getString(R.string.wifi_security_short_wpa2) :
+                            context.getString(R.string.wifi_security_wpa2);
+                    case PSK_WPA_WPA2:
+                        return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) :
+                            context.getString(R.string.wifi_security_wpa_wpa2);
+                    case PSK_UNKNOWN:
+                    default:
+                        return concise ? context.getString(R.string.wifi_security_short_psk_generic)
+                                : context.getString(R.string.wifi_security_psk_generic);
+                }
+            case SECURITY_WEP:
+                return concise ? context.getString(R.string.wifi_security_short_wep) :
+                    context.getString(R.string.wifi_security_wep);
+            case SECURITY_NONE:
+            default:
+                return concise ? "" : context.getString(R.string.wifi_security_none);
+        }
+    }
+
+    public String getSsid() {
+        return ssid;
+    }
+
+    public DetailedState getDetailedState() {
+        return mNetworkInfo != null ? mNetworkInfo.getDetailedState() : null;
+    }
+
+    public String getSummary() {
+        // Update to new summary
+        StringBuilder summary = new StringBuilder();
+
+        if (isActive()) { // This is the active connection
+            summary.append(getSummary(mContext, getDetailedState(),
+                    networkId == WifiConfiguration.INVALID_NETWORK_ID));
+        } else if (mConfig != null
+                && mConfig.hasNoInternetAccess()) {
+            summary.append(mContext.getString(R.string.wifi_no_internet));
+        } else if (mConfig != null && ((mConfig.status == WifiConfiguration.Status.DISABLED &&
+                mConfig.disableReason != WifiConfiguration.DISABLED_UNKNOWN_REASON)
+               || mConfig.autoJoinStatus
+                >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
+            if (mConfig.autoJoinStatus
+                    >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
+                if (mConfig.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE) {
+                    summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+                } else if (mConfig.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) {
+                    summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
+                } else {
+                    summary.append(mContext.getString(R.string.wifi_disabled_wifi_failure));
+                }
+            } else {
+                switch (mConfig.disableReason) {
+                    case WifiConfiguration.DISABLED_AUTH_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
+                        break;
+                    case WifiConfiguration.DISABLED_DHCP_FAILURE:
+                    case WifiConfiguration.DISABLED_DNS_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+                        break;
+                    case WifiConfiguration.DISABLED_UNKNOWN_REASON:
+                    case WifiConfiguration.DISABLED_ASSOCIATION_REJECT:
+                        summary.append(mContext.getString(R.string.wifi_disabled_generic));
+                        break;
+                }
+            }
+        } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
+            summary.append(mContext.getString(R.string.wifi_not_in_range));
+        } else { // In range, not disabled.
+            if (mConfig != null) { // Is saved network
+                summary.append(mContext.getString(R.string.wifi_remembered));
+            }
+        }
+
+        if (WifiTracker.sVerboseLogging > 0) {
+            // Add RSSI/band information for this config, what was seen up to 6 seconds ago
+            // verbose WiFi Logging is only turned on thru developers settings
+            if (mInfo != null && mNetworkInfo != null) { // This is the active connection
+                summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
+            }
+            summary.append(" " + getVisibilityStatus());
+            if (mConfig != null && mConfig.autoJoinStatus > 0) {
+                summary.append(" (" + mConfig.autoJoinStatus);
+                if (mConfig.blackListTimestamp > 0) {
+                    long now = System.currentTimeMillis();
+                    long diff = (now - mConfig.blackListTimestamp)/1000;
+                    long sec = diff%60; //seconds
+                    long min = (diff/60)%60; //minutes
+                    long hour = (min/60)%60; //hours
+                    summary.append(", ");
+                    if (hour > 0) summary.append(Long.toString(hour) + "h ");
+                    summary.append( Long.toString(min) + "m ");
+                    summary.append( Long.toString(sec) + "s ");
+                }
+                summary.append(")");
+            }
+            if (mConfig != null && mConfig.numIpConfigFailures > 0) {
+                summary.append(" ipf=").append(mConfig.numIpConfigFailures);
+            }
+            if (mConfig != null && mConfig.numConnectionFailures > 0) {
+                summary.append(" cf=").append(mConfig.numConnectionFailures);
+            }
+            if (mConfig != null && mConfig.numAuthFailures > 0) {
+                summary.append(" authf=").append(mConfig.numAuthFailures);
+            }
+            if (mConfig != null && mConfig.numNoInternetAccessReports > 0) {
+                summary.append(" noInt=").append(mConfig.numNoInternetAccessReports);
+            }
+        }
+        return summary.toString();
+    }
+
+    /**
+     * Returns the visibility status of the WifiConfiguration.
+     *
+     * @return autojoin debugging information
+     * TODO: use a string formatter
+     * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
+     * For instance [-40,5/-30,2]
+     */
+    private String getVisibilityStatus() {
+        StringBuilder visibility = new StringBuilder();
+        StringBuilder scans24GHz = null;
+        StringBuilder scans5GHz = null;
+        String bssid = null;
+
+        long now = System.currentTimeMillis();
+
+        if (mInfo != null) {
+            bssid = mInfo.getBSSID();
+            if (bssid != null) {
+                visibility.append(" ").append(bssid);
+            }
+            visibility.append(" rssi=").append(mInfo.getRssi());
+            visibility.append(" ");
+            visibility.append(" score=").append(mInfo.score);
+            visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate));
+            visibility.append(String.format("%.1f,", mInfo.txRetriesRate));
+            visibility.append(String.format("%.1f ", mInfo.txBadRate));
+            visibility.append(String.format("rx=%.1f", mInfo.rxSuccessRate));
+        }
+
+        if (mScanResultCache != null) {
+            int rssi5 = WifiConfiguration.INVALID_RSSI;
+            int rssi24 = WifiConfiguration.INVALID_RSSI;
+            int num5 = 0;
+            int num24 = 0;
+            int numBlackListed = 0;
+            int n24 = 0; // Number scan results we included in the string
+            int n5 = 0; // Number scan results we included in the string
+            Map<String, ScanResult> list = mScanResultCache.snapshot();
+            // TODO: sort list by RSSI or age
+            for (ScanResult result : list.values()) {
+                if (result.seen == 0)
+                    continue;
+
+                if (result.autoJoinStatus != ScanResult.ENABLED) numBlackListed++;
+
+                if (result.frequency >= LOWER_FREQ_5GHZ
+                        && result.frequency <= HIGHER_FREQ_5GHZ) {
+                    // Strictly speaking: [4915, 5825]
+                    // number of known BSSID on 5GHz band
+                    num5 = num5 + 1;
+                } else if (result.frequency >= LOWER_FREQ_24GHZ
+                        && result.frequency <= HIGHER_FREQ_24GHZ) {
+                    // Strictly speaking: [2412, 2482]
+                    // number of known BSSID on 2.4Ghz band
+                    num24 = num24 + 1;
+                }
+
+                // Ignore results seen, older than 20 seconds
+                if (now - result.seen > VISIBILITY_OUTDATED_AGE_IN_MILLI) continue;
+
+                if (result.frequency >= LOWER_FREQ_5GHZ
+                        && result.frequency <= HIGHER_FREQ_5GHZ) {
+                    if (result.level > rssi5) {
+                        rssi5 = result.level;
+                    }
+                    if (n5 < 4) {
+                        if (scans5GHz == null) scans5GHz = new StringBuilder();
+                        scans5GHz.append(" \n{").append(result.BSSID);
+                        if (bssid != null && result.BSSID.equals(bssid)) scans5GHz.append("*");
+                        scans5GHz.append("=").append(result.frequency);
+                        scans5GHz.append(",").append(result.level);
+                        if (result.autoJoinStatus != 0) {
+                            scans5GHz.append(",st=").append(result.autoJoinStatus);
+                        }
+                        if (result.numIpConfigFailures != 0) {
+                            scans5GHz.append(",ipf=").append(result.numIpConfigFailures);
+                        }
+                        scans5GHz.append("}");
+                        n5++;
+                    }
+                } else if (result.frequency >= LOWER_FREQ_24GHZ
+                        && result.frequency <= HIGHER_FREQ_24GHZ) {
+                    if (result.level > rssi24) {
+                        rssi24 = result.level;
+                    }
+                    if (n24 < 4) {
+                        if (scans24GHz == null) scans24GHz = new StringBuilder();
+                        scans24GHz.append(" \n{").append(result.BSSID);
+                        if (bssid != null && result.BSSID.equals(bssid)) scans24GHz.append("*");
+                        scans24GHz.append("=").append(result.frequency);
+                        scans24GHz.append(",").append(result.level);
+                        if (result.autoJoinStatus != 0) {
+                            scans24GHz.append(",st=").append(result.autoJoinStatus);
+                        }
+                        if (result.numIpConfigFailures != 0) {
+                            scans24GHz.append(",ipf=").append(result.numIpConfigFailures);
+                        }
+                        scans24GHz.append("}");
+                        n24++;
+                    }
+                }
+            }
+            visibility.append(" [");
+            if (num24 > 0) {
+                visibility.append("(").append(num24).append(")");
+                if (n24 <= 4) {
+                    if (scans24GHz != null) {
+                        visibility.append(scans24GHz.toString());
+                    }
+                } else {
+                    visibility.append("max=").append(rssi24);
+                    if (scans24GHz != null) {
+                        visibility.append(",").append(scans24GHz.toString());
+                    }
+                }
+            }
+            visibility.append(";");
+            if (num5 > 0) {
+                visibility.append("(").append(num5).append(")");
+                if (n5 <= 4) {
+                    if (scans5GHz != null) {
+                        visibility.append(scans5GHz.toString());
+                    }
+                } else {
+                    visibility.append("max=").append(rssi5);
+                    if (scans5GHz != null) {
+                        visibility.append(",").append(scans5GHz.toString());
+                    }
+                }
+            }
+            if (numBlackListed > 0)
+                visibility.append("!").append(numBlackListed);
+            visibility.append("]");
+        } else {
+            if (mRssi != Integer.MAX_VALUE) {
+                visibility.append(" rssi=");
+                visibility.append(mRssi);
+                if (mScanResult != null) {
+                    visibility.append(", f=");
+                    visibility.append(mScanResult.frequency);
+                }
+            }
+        }
+
+        return visibility.toString();
+    }
+
+    /**
+     * Return whether this is the active connection.
+     * For ephemeral connections (networkId is invalid), this returns false if the network is
+     * disconnected.
+     */
+    public boolean isActive() {
+        return mNetworkInfo != null &&
+                (networkId != WifiConfiguration.INVALID_NETWORK_ID ||
+                 mNetworkInfo.getState() != State.DISCONNECTED);
+    }
+
+    public boolean isConnectable() {
+        return getLevel() != -1 && getDetailedState() == null;
+    }
+
+    public boolean isEphemeral() {
+        return !isSaved() && mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
+    }
+
+    /** Return whether the given {@link WifiInfo} is for this access point. */
+    private boolean isInfoForThisAccessPoint(WifiInfo info) {
+        if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+            return networkId == info.getNetworkId();
+        } else {
+            // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
+            // (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
+            // TODO: Handle hex string SSIDs.
+            return ssid.equals(removeDoubleQuotes(info.getSSID()));
+        }
+    }
+
+    public boolean isSaved() {
+        return networkId != WifiConfiguration.INVALID_NETWORK_ID;
+    }
+
+    public Object getTag() {
+        return mTag;
+    }
+
+    public void setTag(Object tag) {
+        mTag = tag;
+    }
+
+    /**
+     * Generate and save a default wifiConfiguration with common values.
+     * Can only be called for unsecured networks.
+     */
+    public void generateOpenNetworkConfig() {
+        if (security != SECURITY_NONE)
+            throw new IllegalStateException();
+        if (mConfig != null)
+            return;
+        mConfig = new WifiConfiguration();
+        mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
+        mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+    }
+
+    void loadConfig(WifiConfiguration config) {
+        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
+        security = getSecurity(config);
+        networkId = config.networkId;
+        mConfig = config;
+    }
+
+    private void loadResult(ScanResult result) {
+        ssid = result.SSID;
+        security = getSecurity(result);
+        if (security == SECURITY_PSK)
+            pskType = getPskType(result);
+        mRssi = result.level;
+        mScanResult = result;
+        if (result.seen > mSeen) {
+            mSeen = result.seen;
+        }
+    }
+
+    public void saveWifiState(Bundle savedState) {
+        savedState.putParcelable(KEY_CONFIG, mConfig);
+        savedState.putParcelable(KEY_SCANRESULT, mScanResult);
+        savedState.putParcelable(KEY_WIFIINFO, mInfo);
+        if (mNetworkInfo != null) {
+            savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
+        }
+    }
+
+    public void setListener(AccessPointListener listener) {
+        mAccessPointListener = listener;
+    }
+
+    boolean update(ScanResult result) {
+        if (result.seen > mSeen) {
+            mSeen = result.seen;
+        }
+        if (WifiTracker.sVerboseLogging > 0) {
+            if (mScanResultCache == null) {
+                mScanResultCache = new LruCache<String, ScanResult>(32);
+            }
+            mScanResultCache.put(result.BSSID, result);
+        }
+
+        if (ssid.equals(result.SSID) && security == getSecurity(result)) {
+            if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) {
+                int oldLevel = getLevel();
+                mRssi = result.level;
+                if (getLevel() != oldLevel && mAccessPointListener != null) {
+                    mAccessPointListener.onLevelChanged(this);
+                }
+            }
+            // This flag only comes from scans, is not easily saved in config
+            if (security == SECURITY_PSK) {
+                pskType = getPskType(result);
+            }
+            mScanResult = result;
+            if (mAccessPointListener != null) {
+                mAccessPointListener.onAccessPointChanged(this);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    boolean update(WifiInfo info, NetworkInfo networkInfo) {
+        boolean reorder = false;
+        if (info != null && isInfoForThisAccessPoint(info)) {
+            reorder = (mInfo == null);
+            mRssi = info.getRssi();
+            mInfo = info;
+            mNetworkInfo = networkInfo;
+            if (mAccessPointListener != null) {
+                mAccessPointListener.onAccessPointChanged(this);
+            }
+        } else if (mInfo != null) {
+            reorder = true;
+            mInfo = null;
+            mNetworkInfo = null;
+            if (mAccessPointListener != null) {
+                mAccessPointListener.onAccessPointChanged(this);
+            }
+        }
+        return reorder;
+    }
+
+    public static String getSummary(Context context, String ssid, DetailedState state,
+            boolean isEphemeral) {
+        if (state == DetailedState.CONNECTED && isEphemeral && ssid == null) {
+            // Special case for connected + ephemeral networks.
+            return context.getString(R.string.connected_via_wfa);
+        }
+
+        String[] formats = context.getResources().getStringArray((ssid == null)
+                ? R.array.wifi_status : R.array.wifi_status_with_ssid);
+        int index = state.ordinal();
+
+        if (index >= formats.length || formats[index].length() == 0) {
+            return null;
+        }
+        return String.format(formats[index], ssid);
+    }
+
+    public static String getSummary(Context context, DetailedState state, boolean isEphemeral) {
+        return getSummary(context, null, state, isEphemeral);
+    }
+
+    public static String convertToQuotedString(String string) {
+        return "\"" + string + "\"";
+    }
+
+    private static int getPskType(ScanResult result) {
+        boolean wpa = result.capabilities.contains("WPA-PSK");
+        boolean wpa2 = result.capabilities.contains("WPA2-PSK");
+        if (wpa2 && wpa) {
+            return PSK_WPA_WPA2;
+        } else if (wpa2) {
+            return PSK_WPA2;
+        } else if (wpa) {
+            return PSK_WPA;
+        } else {
+            Log.w(TAG, "Received abnormal flag string: " + result.capabilities);
+            return PSK_UNKNOWN;
+        }
+    }
+
+    private static int getSecurity(ScanResult result) {
+        if (result.capabilities.contains("WEP")) {
+            return SECURITY_WEP;
+        } else if (result.capabilities.contains("PSK")) {
+            return SECURITY_PSK;
+        } else if (result.capabilities.contains("EAP")) {
+            return SECURITY_EAP;
+        }
+        return SECURITY_NONE;
+    }
+
+    static int getSecurity(WifiConfiguration config) {
+        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+            return SECURITY_PSK;
+        }
+        if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+            return SECURITY_EAP;
+        }
+        return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
+    }
+
+    public static String securityToString(int security, int pskType) {
+        if (security == SECURITY_WEP) {
+            return "WEP";
+        } else if (security == SECURITY_PSK) {
+            if (pskType == PSK_WPA) {
+                return "WPA";
+            } else if (pskType == PSK_WPA2) {
+                return "WPA2";
+            } else if (pskType == PSK_WPA_WPA2) {
+                return "WPA_WPA2";
+            }
+            return "PSK";
+        } else if (security == SECURITY_EAP) {
+            return "EAP";
+        }
+        return "NONE";
+    }
+
+    static String removeDoubleQuotes(String string) {
+        int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"')
+                && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
+    }
+
+    public interface AccessPointListener {
+        void onAccessPointChanged(AccessPoint accessPoint);
+        void onLevelChanged(AccessPoint accessPoint);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
new file mode 100644
index 0000000..c3e23d2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.wifi;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Message;
+import android.widget.Toast;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.R;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Tracks saved or available wifi networks and their state.
+ */
+public class WifiTracker {
+    private static final String TAG = "WifiTracker";
+
+    /** verbose logging flag. this flag is set thru developer debugging options
+     * and used so as to assist with in-the-field WiFi connectivity debugging  */
+    public static int sVerboseLogging = 0;
+
+    // TODO: Allow control of this?
+    // Combo scans can take 5-6s to complete - set to 10s.
+    private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
+
+    private final Context mContext;
+    private final WifiManager mWifiManager;
+    private final IntentFilter mFilter;
+
+    private final AtomicBoolean mConnected = new AtomicBoolean(false);
+    private final WifiListener mListener;
+    private final boolean mIncludeSaved;
+    private final boolean mIncludeScans;
+
+    private boolean mSavedNetworksExist;
+    private boolean mRegistered;
+    private ArrayList<AccessPoint> mAccessPoints = new ArrayList<>();
+    private ArrayList<AccessPoint> mCachedAccessPoints = new ArrayList<>();
+
+    private NetworkInfo mLastNetworkInfo;
+    private WifiInfo mLastInfo;
+
+    @VisibleForTesting
+    Scanner mScanner;
+
+    public WifiTracker(Context context, WifiListener wifiListener, boolean includeSaved,
+            boolean includeScans) {
+        this(context, wifiListener, includeSaved, includeScans,
+                (WifiManager) context.getSystemService(Context.WIFI_SERVICE));
+    }
+
+    @VisibleForTesting
+    WifiTracker(Context context, WifiListener wifiListener, boolean includeSaved,
+            boolean includeScans, WifiManager wifiManager) {
+        if (!includeSaved && !includeScans) {
+            throw new IllegalArgumentException("Must include either saved or scans");
+        }
+        mContext = context;
+        mWifiManager = wifiManager;
+        mIncludeSaved = includeSaved;
+        mIncludeScans = includeScans;
+        mListener = wifiListener;
+
+        // check if verbose logging has been turned on or off
+        sVerboseLogging = mWifiManager.getVerboseLoggingLevel();
+
+        mFilter = new IntentFilter();
+        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
+        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+    }
+
+    /**
+     * Forces an update of the wifi networks when not scanning.
+     */
+    public void forceUpdate() {
+        updateAccessPoints();
+    }
+
+    /**
+     * Force a scan for wifi networks to happen now.
+     */
+    public void forceScan() {
+        if (mWifiManager.isWifiEnabled() && mScanner != null) {
+            mScanner.forceScan();
+        }
+    }
+
+    /**
+     * Temporarily stop scanning for wifi networks.
+     */
+    public void pauseScanning() {
+        if (mScanner != null) {
+            mScanner.pause();
+        }
+    }
+
+    /**
+     * Resume scanning for wifi networks after it has been paused.
+     */
+    public void resumeScanning() {
+        if (mWifiManager.isWifiEnabled()) {
+            if (mScanner == null) {
+                mScanner = new Scanner();
+            }
+            mScanner.resume();
+        }
+        updateAccessPoints();
+    }
+
+    /**
+     * Start tracking wifi networks.
+     * Registers listeners and starts scanning for wifi networks. If this is not called
+     * then forceUpdate() must be called to populate getAccessPoints().
+     */
+    public void startTracking() {
+        resumeScanning();
+        if (!mRegistered) {
+            mContext.registerReceiver(mReceiver, mFilter);
+            mRegistered = true;
+        }
+    }
+
+    /**
+     * Stop tracking wifi networks.
+     * Unregisters all listeners and stops scanning for wifi networks. This should always
+     * be called when done with a WifiTracker (if startTracking was called) to ensure
+     * proper cleanup.
+     */
+    public void stopTracking() {
+        if (mRegistered) {
+            mContext.unregisterReceiver(mReceiver);
+            mRegistered = false;
+        }
+        pauseScanning();
+    }
+
+    /**
+     * Gets the current list of access points.
+     */
+    public List<AccessPoint> getAccessPoints() {
+        return mAccessPoints;
+    }
+
+    public WifiManager getManager() {
+        return mWifiManager;
+    }
+
+    public boolean isWifiEnabled() {
+        return mWifiManager.isWifiEnabled();
+    }
+
+    /**
+     * @return true when there are saved networks on the device, regardless
+     * of whether the WifiTracker is tracking saved networks.
+     */
+    public boolean doSavedNetworksExist() {
+        return mSavedNetworksExist;
+    }
+
+    public boolean isConnected() {
+        return mConnected.get();
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("  - wifi tracker ------");
+        for (AccessPoint accessPoint : mAccessPoints) {
+            pw.println("  " + accessPoint);
+        }
+    }
+
+    private void updateAccessPoints() {
+        // Swap the current access points into a cached list.
+        ArrayList<AccessPoint> tmpSwp = mAccessPoints;
+        mAccessPoints = mCachedAccessPoints;
+        mCachedAccessPoints = tmpSwp;
+        // Clear out the configs so we don't think something is saved when it isn't.
+        for (AccessPoint accessPoint : mCachedAccessPoints) {
+            accessPoint.clearConfig();
+        }
+
+        mAccessPoints.clear();
+
+        /** Lookup table to more quickly update AccessPoints by only considering objects with the
+         * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
+        Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
+
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        if (configs != null) {
+            mSavedNetworksExist = configs.size() != 0;
+            for (WifiConfiguration config : configs) {
+                if (config.selfAdded && config.numAssociation == 0) {
+                    continue;
+                }
+                AccessPoint accessPoint = getCachedOrCreate(config);
+                if (mLastInfo != null && mLastNetworkInfo != null) {
+                    accessPoint.update(mLastInfo, mLastNetworkInfo);
+                }
+                if (mIncludeSaved) {
+                    mAccessPoints.add(accessPoint);
+                    apMap.put(accessPoint.getSsid(), accessPoint);
+                } else {
+                    // If we aren't using saved networks, drop them into the cache so that
+                    // we have access to their saved info.
+                    mCachedAccessPoints.add(accessPoint);
+                }
+            }
+        }
+
+        final List<ScanResult> results = mWifiManager.getScanResults();
+        if (results != null) {
+            for (ScanResult result : results) {
+                // Ignore hidden and ad-hoc networks.
+                if (result.SSID == null || result.SSID.length() == 0 ||
+                        result.capabilities.contains("[IBSS]")) {
+                    continue;
+                }
+
+                boolean found = false;
+                for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
+                    if (accessPoint.update(result)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found && mIncludeScans) {
+                    AccessPoint accessPoint = getCachedOrCreate(result);
+                    if (mLastInfo != null && mLastNetworkInfo != null) {
+                        accessPoint.update(mLastInfo, mLastNetworkInfo);
+                    }
+                    mAccessPoints.add(accessPoint);
+                    apMap.put(accessPoint.getSsid(), accessPoint);
+                }
+            }
+        }
+
+        // Pre-sort accessPoints to speed preference insertion
+        Collections.sort(mAccessPoints);
+        if (mListener != null) {
+            mListener.onAccessPointsChanged();
+        }
+    }
+
+    private AccessPoint getCachedOrCreate(ScanResult result) {
+        final int N = mCachedAccessPoints.size();
+        for (int i = 0; i < N; i++) {
+            if (mCachedAccessPoints.get(i).matches(result)) {
+                AccessPoint ret = mCachedAccessPoints.remove(i);
+                ret.update(result);
+                return ret;
+            }
+        }
+        return new AccessPoint(mContext, result);
+    }
+
+    private AccessPoint getCachedOrCreate(WifiConfiguration config) {
+        final int N = mCachedAccessPoints.size();
+        for (int i = 0; i < N; i++) {
+            if (mCachedAccessPoints.get(i).matches(config)) {
+                AccessPoint ret = mCachedAccessPoints.remove(i);
+                ret.loadConfig(config);
+                return ret;
+            }
+        }
+        return new AccessPoint(mContext, config);
+    }
+
+    private void updateNetworkInfo(NetworkInfo networkInfo) {
+        /* sticky broadcasts can call this when wifi is disabled */
+        if (!mWifiManager.isWifiEnabled()) {
+            mScanner.pause();
+            return;
+        }
+
+        if (networkInfo != null &&
+                networkInfo.getDetailedState() == DetailedState.OBTAINING_IPADDR) {
+            mScanner.pause();
+        } else {
+            mScanner.resume();
+        }
+
+        mLastInfo = mWifiManager.getConnectionInfo();
+        if (networkInfo != null) {
+            mLastNetworkInfo = networkInfo;
+        }
+
+        boolean reorder = false;
+        for (int i = mAccessPoints.size() - 1; i >= 0; --i) {
+            if (mAccessPoints.get(i).update(mLastInfo, mLastNetworkInfo)) {
+                reorder = true;
+            }
+        }
+        if (reorder) {
+            Collections.sort(mAccessPoints);
+            if (mListener != null) {
+                mListener.onAccessPointsChanged();
+            }
+        }
+    }
+
+    private void updateWifiState(int state) {
+        if (state == WifiManager.WIFI_STATE_ENABLED) {
+            mScanner.resume();
+        } else {
+            mLastInfo = null;
+            mLastNetworkInfo = null;
+            mScanner.pause();
+        }
+        if (mListener != null) {
+            mListener.onWifiStateChanged(state);
+        }
+    }
+
+    public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
+            boolean includeScans) {
+        WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
+        tracker.forceUpdate();
+        return tracker.getAccessPoints();
+    }
+
+    @VisibleForTesting
+    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+                updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                        WifiManager.WIFI_STATE_UNKNOWN));
+            } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
+                    WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
+                    WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
+                updateAccessPoints();
+            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
+                        WifiManager.EXTRA_NETWORK_INFO);
+                mConnected.set(info.isConnected());
+                if (mListener != null) {
+                    mListener.onConnectedChanged();
+                }
+                updateAccessPoints();
+                updateNetworkInfo(info);
+            } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
+                updateNetworkInfo(null);
+            }
+        }
+    };
+
+    @VisibleForTesting
+    class Scanner extends Handler {
+        private int mRetry = 0;
+
+        void resume() {
+            if (!hasMessages(0)) {
+                sendEmptyMessage(0);
+            }
+        }
+
+        void forceScan() {
+            removeMessages(0);
+            sendEmptyMessage(0);
+        }
+
+        void pause() {
+            mRetry = 0;
+            removeMessages(0);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            if (mWifiManager.startScan()) {
+                mRetry = 0;
+            } else if (++mRetry >= 3) {
+                mRetry = 0;
+                if (mContext != null) {
+                    Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
+                }
+                return;
+            }
+            sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
+        }
+    }
+
+    /** A restricted multimap for use in constructAccessPoints */
+    private static class Multimap<K,V> {
+        private final HashMap<K,List<V>> store = new HashMap<K,List<V>>();
+        /** retrieve a non-null list of values with key K */
+        List<V> getAll(K key) {
+            List<V> values = store.get(key);
+            return values != null ? values : Collections.<V>emptyList();
+        }
+
+        void put(K key, V val) {
+            List<V> curVals = store.get(key);
+            if (curVals == null) {
+                curVals = new ArrayList<V>(3);
+                store.put(key, curVals);
+            }
+            curVals.add(val);
+        }
+    }
+
+    public interface WifiListener {
+        /**
+         * Called when the state of Wifi has changed, the state will be one of
+         * the following.
+         *
+         * <li>{@link WifiManager#WIFI_STATE_DISABLED}</li>
+         * <li>{@link WifiManager#WIFI_STATE_ENABLED}</li>
+         * <li>{@link WifiManager#WIFI_STATE_DISABLING}</li>
+         * <li>{@link WifiManager#WIFI_STATE_ENABLING}</li>
+         * <li>{@link WifiManager#WIFI_STATE_UNKNOWN}</li>
+         * <p>
+         *
+         * @param state The new state of wifi.
+         */
+        void onWifiStateChanged(int state);
+
+        /**
+         * Called when the connection state of wifi has changed and isConnected
+         * should be called to get the updated state.
+         */
+        void onConnectedChanged();
+
+        /**
+         * Called to indicate the list of AccessPoints has been updated and
+         * getAccessPoints should be called to get the latest information.
+         */
+        void onAccessPointsChanged();
+    }
+}
diff --git a/packages/SettingsLib/tests/Android.mk b/packages/SettingsLib/tests/Android.mk
new file mode 100644
index 0000000..d3ffffa
--- /dev/null
+++ b/packages/SettingsLib/tests/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+
+LOCAL_PACKAGE_NAME := SettingsLibTests
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
+
+include frameworks/base/packages/SettingsLib/common.mk
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SettingsLib/tests/AndroidManifest.xml b/packages/SettingsLib/tests/AndroidManifest.xml
new file mode 100644
index 0000000..00d16fc
--- /dev/null
+++ b/packages/SettingsLib/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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.settingslib">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.settingslib"
+        android:label="Tests for SettingsLib">
+    </instrumentation>
+</manifest>
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/BaseTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/BaseTest.java
new file mode 100644
index 0000000..04a568e
--- /dev/null
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/BaseTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib;
+
+import android.test.AndroidTestCase;
+
+public class BaseTest extends AndroidTestCase {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // Mockito stuff.
+        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+    }
+
+}
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/wifi/AccessPointTest.java
new file mode 100644
index 0000000..4ac461d
--- /dev/null
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.wifi;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+
+import com.android.settingslib.BaseTest;
+import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+// TODO: Add some coverage
+public class AccessPointTest extends BaseTest {
+
+    private static final String TEST_SSID = "TestSsid";
+    private static final int NETWORK_ID = 0;
+
+    private AccessPointListener mAccessPointListener;
+    private AccessPoint mAccessPoint;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mAccessPointListener = Mockito.mock(AccessPointListener.class);
+
+        WifiConfiguration wifiConfig = new WifiConfiguration();
+        wifiConfig.networkId = NETWORK_ID;
+        wifiConfig.SSID = TEST_SSID;
+
+        mAccessPoint = new AccessPoint(mContext, wifiConfig);
+        mAccessPoint.setListener(mAccessPointListener);
+    }
+
+    public void testOnLevelChanged() {
+        ScanResult result = new ScanResult();
+        result.capabilities = "";
+        result.SSID = TEST_SSID;
+
+        // Give it a level.
+        result.level = WifiTrackerTest.levelToRssi(1);
+        mAccessPoint.update(result);
+        verifyOnLevelChangedCallback(1);
+
+        // Give it a better level.
+        result.level = WifiTrackerTest.levelToRssi(2);
+        mAccessPoint.update(result);
+        verifyOnLevelChangedCallback(1);
+    }
+
+    public void testOnAccessPointChangedCallback() {
+        WifiInfo wifiInfo = Mockito.mock(WifiInfo.class);
+        Mockito.when(wifiInfo.getNetworkId()).thenReturn(NETWORK_ID);
+
+        mAccessPoint.update(wifiInfo, null);
+        verifyOnAccessPointsCallback(1);
+
+        mAccessPoint.update(null, null);
+        verifyOnAccessPointsCallback(2);
+
+        ScanResult result = new ScanResult();
+        result.capabilities = "";
+        result.SSID = TEST_SSID;
+        mAccessPoint.update(result);
+        verifyOnAccessPointsCallback(3);
+    }
+
+    private void verifyOnLevelChangedCallback(int num) {
+        ArgumentCaptor<AccessPoint> accessPoint = ArgumentCaptor.forClass(AccessPoint.class);
+        Mockito.verify(mAccessPointListener, Mockito.atLeast(num))
+                .onLevelChanged(accessPoint.capture());
+        assertEquals(mAccessPoint, accessPoint.getValue());
+    }
+
+    private void verifyOnAccessPointsCallback(int num) {
+        ArgumentCaptor<AccessPoint> accessPoint = ArgumentCaptor.forClass(AccessPoint.class);
+        Mockito.verify(mAccessPointListener, Mockito.atLeast(num))
+                .onAccessPointChanged(accessPoint.capture());
+        assertEquals(mAccessPoint, accessPoint.getValue());
+    }
+
+}
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/wifi/WifiTrackerTest.java
new file mode 100644
index 0000000..73d4938
--- /dev/null
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.wifi;
+
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.util.Log;
+
+import com.android.settingslib.BaseTest;
+import com.android.settingslib.wifi.WifiTracker.WifiListener;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class WifiTrackerTest extends BaseTest {
+
+    private static final String TAG = "WifiTrackerTest";
+
+    private static final String[] TEST_SSIDS = new String[] {
+        "TEST_SSID_1",
+        "TEST_SSID_2",
+        "TEST_SSID_3",
+        "TEST_SSID_4",
+        "TEST_SSID_5",
+    };
+    private static final int NUM_NETWORKS = 5;
+
+    private WifiManager mWifiManager;
+    private WifiListener mWifiListener;
+
+    private WifiTracker mWifiTracker;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mWifiManager = Mockito.mock(WifiManager.class);
+        mWifiListener = Mockito.mock(WifiListener.class);
+        mWifiTracker = new WifiTracker(mContext, mWifiListener, true, true, mWifiManager);
+        mWifiTracker.mScanner = mWifiTracker.new Scanner();
+        Mockito.when(mWifiManager.isWifiEnabled()).thenReturn(true);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        mWifiTracker.dump(pw);
+        pw.flush();
+        Log.d(TAG, sw.toString());
+        super.tearDown();
+    }
+
+    public void testAccessPointsCallback() {
+        sendScanResultsAvailable();
+
+        Mockito.verify(mWifiListener, Mockito.atLeastOnce()).onAccessPointsChanged();
+    }
+
+    public void testConnectedCallback() {
+        sendConnected();
+
+        Mockito.verify(mWifiListener, Mockito.atLeastOnce()).onConnectedChanged();
+        assertEquals(true, mWifiTracker.isConnected());
+    }
+
+    public void testWifiStateCallback() {
+        final int TEST_WIFI_STATE = WifiManager.WIFI_STATE_ENABLED;
+
+        Intent i = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        i.putExtra(WifiManager.EXTRA_WIFI_STATE, TEST_WIFI_STATE);
+        mWifiTracker.mReceiver.onReceive(mContext, i);
+
+        ArgumentCaptor<Integer> wifiState = ArgumentCaptor.forClass(Integer.class);
+        Mockito.verify(mWifiListener, Mockito.atLeastOnce())
+                .onWifiStateChanged(wifiState.capture());
+        assertEquals(TEST_WIFI_STATE, (int) wifiState.getValue());
+    }
+
+    public void testScanner() {
+        // TODO: Figure out how to verify more of the Scanner functionality.
+        // Make scans be successful.
+        Mockito.when(mWifiManager.startScan()).thenReturn(true);
+
+        mWifiTracker.mScanner.handleMessage(null);
+        Mockito.verify(mWifiManager, Mockito.atLeastOnce()).startScan();
+    }
+
+    public void testNetworkSorting() {
+        List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>();
+        List<ScanResult> scanResults = new ArrayList<ScanResult>();
+        String[] expectedSsids = generateTestNetworks(wifiConfigs, scanResults, true);
+
+        // Tell WifiTracker we are connected now.
+        sendConnected();
+
+        // Send all of the configs and scan results to the tracker.
+        Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs);
+        Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults);
+        sendScanResultsAvailable();
+
+        List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
+        assertEquals("Expected number of results", NUM_NETWORKS, accessPoints.size());
+        for (int i = 0; i < NUM_NETWORKS; i++) {
+            assertEquals("Verifying slot " + i, expectedSsids[i], accessPoints.get(i).getSsid());
+        }
+    }
+
+    public void testSavedOnly() {
+        mWifiTracker = new WifiTracker(mContext, mWifiListener, true, false, mWifiManager);
+        mWifiTracker.mScanner = mWifiTracker.new Scanner();
+
+        List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>();
+        List<ScanResult> scanResults = new ArrayList<ScanResult>();
+        generateTestNetworks(wifiConfigs, scanResults, true);
+
+        // Tell WifiTracker we are connected now.
+        sendConnected();
+
+        // Send all of the configs and scan results to the tracker.
+        Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs);
+        Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults);
+        sendScanResultsAvailable();
+
+        List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
+        // Only expect the first two to come back in the results.
+        assertEquals("Expected number of results", 2, accessPoints.size());
+        assertEquals(TEST_SSIDS[1], accessPoints.get(0).getSsid());
+        assertEquals(TEST_SSIDS[0], accessPoints.get(1).getSsid());
+    }
+
+    public void testAvailableOnly() {
+        mWifiTracker = new WifiTracker(mContext, mWifiListener, false, true, mWifiManager);
+        mWifiTracker.mScanner = mWifiTracker.new Scanner();
+
+        List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>();
+        List<ScanResult> scanResults = new ArrayList<ScanResult>();
+        String[] expectedSsids = generateTestNetworks(wifiConfigs, scanResults, true);
+
+        // Tell WifiTracker we are connected now.
+        sendConnected();
+
+        // Send all of the configs and scan results to the tracker.
+        Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs);
+        Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults);
+        sendScanResultsAvailable();
+
+        // Expect the last one (sorted order) to be left off since its only saved.
+        List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
+        assertEquals("Expected number of results", NUM_NETWORKS - 1, accessPoints.size());
+        for (int i = 0; i < NUM_NETWORKS - 1; i++) {
+            assertEquals("Verifying slot " + i, expectedSsids[i], accessPoints.get(i).getSsid());
+        }
+    }
+
+    public void testNonEphemeralConnected() {
+        mWifiTracker = new WifiTracker(mContext, mWifiListener, false, true, mWifiManager);
+        mWifiTracker.mScanner = mWifiTracker.new Scanner();
+
+        List<WifiConfiguration> wifiConfigs = new ArrayList<WifiConfiguration>();
+        List<ScanResult> scanResults = new ArrayList<ScanResult>();
+        String[] expectedSsids = generateTestNetworks(wifiConfigs, scanResults, false);
+
+        // Tell WifiTracker we are connected now.
+        sendConnected();
+
+        // Send all of the configs and scan results to the tracker.
+        Mockito.when(mWifiManager.getConfiguredNetworks()).thenReturn(wifiConfigs);
+        Mockito.when(mWifiManager.getScanResults()).thenReturn(scanResults);
+        sendScanResultsAvailable();
+        // Do this twice to catch a bug that was happening in the caching, making things ephemeral.
+        sendScanResultsAvailable();
+
+        List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
+        assertEquals("Expected number of results", NUM_NETWORKS - 1, accessPoints.size());
+        assertFalse("Connection is not ephemeral", accessPoints.get(0).isEphemeral());
+        assertTrue("Connected to wifi", accessPoints.get(0).isActive());
+    }
+
+    private String[] generateTestNetworks(List<WifiConfiguration> wifiConfigs,
+            List<ScanResult> scanResults, boolean connectedIsEphemeral) {
+        String[] expectedSsids = new String[NUM_NETWORKS];
+
+        // First is just saved;
+        addConfig(wifiConfigs, TEST_SSIDS[0]);
+        // This should come last since its not available.
+        expectedSsids[4] = TEST_SSIDS[0];
+
+        // Second is saved and available.
+        addConfig(wifiConfigs, TEST_SSIDS[1]);
+        addResult(scanResults, TEST_SSIDS[1], 0);
+        // This one is going to have a couple extra results, to verify de-duplication.
+        addResult(scanResults, TEST_SSIDS[1], 2);
+        addResult(scanResults, TEST_SSIDS[1], 1);
+        // This should come second since it is available and saved but not connected.
+        expectedSsids[1] = TEST_SSIDS[1];
+
+        // Third is just available, but higher rssi.
+        addResult(scanResults, TEST_SSIDS[2], 3);
+        // This comes after the next one since it has a lower rssi.
+        expectedSsids[3] = TEST_SSIDS[2];
+
+        // Fourth also just available but with even higher rssi.
+        addResult(scanResults, TEST_SSIDS[3], 4);
+        // This is the highest rssi but not saved so it should be after the saved+availables.
+        expectedSsids[2] = TEST_SSIDS[3];
+
+        // Last is going to be connected.
+        int netId = WifiConfiguration.INVALID_NETWORK_ID;
+        if (!connectedIsEphemeral) {
+            netId = addConfig(wifiConfigs, TEST_SSIDS[4]);
+        }
+        addResult(scanResults, TEST_SSIDS[4], 2);
+        // Setup wifi connection to be this one.
+        WifiInfo wifiInfo = Mockito.mock(WifiInfo.class);
+        Mockito.when(wifiInfo.getSSID()).thenReturn(TEST_SSIDS[4]);
+        Mockito.when(wifiInfo.getNetworkId()).thenReturn(netId);
+        Mockito.when(mWifiManager.getConnectionInfo()).thenReturn(wifiInfo);
+        // This should come first since it is connected.
+        expectedSsids[0] = TEST_SSIDS[4];
+
+        return expectedSsids;
+    }
+
+    private void addResult(List<ScanResult> results, String ssid, int level) {
+        results.add(new ScanResult(WifiSsid.createFromAsciiEncoded(ssid),
+                ssid, ssid, levelToRssi(level), AccessPoint.LOWER_FREQ_24GHZ, 0));
+    }
+
+    public static int levelToRssi(int level) {
+        // Reverse level to rssi calculation based off from WifiManager.calculateSignalLevel.
+        final int MAX_RSSI = -55;
+        final int MIN_RSSI = -100;
+        final int NUM_LEVELS = 4;
+        return level * (MAX_RSSI - MIN_RSSI) / (NUM_LEVELS - 1) + MIN_RSSI;
+    }
+
+    private int addConfig(List<WifiConfiguration> configs, String ssid) {
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = configs.size();
+        config.SSID = '"' + ssid + '"';
+        configs.add(config);
+        return config.networkId;
+    }
+
+    private void sendConnected() {
+        NetworkInfo networkInfo = Mockito.mock(NetworkInfo.class);
+        Mockito.when(networkInfo.isConnected()).thenReturn(true);
+        Mockito.when(networkInfo.getState()).thenReturn(State.CONNECTED);
+        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
+        mWifiTracker.mReceiver.onReceive(mContext, intent);
+    }
+
+    private void sendScanResultsAvailable() {
+        Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        mWifiTracker.mReceiver.onReceive(mContext, i);
+    }
+
+}
diff --git a/packages/SettingsProvider/res/values-tl/strings.xml b/packages/SettingsProvider/res/values-tl/strings.xml
index 19219ec..0fe535e 100644
--- a/packages/SettingsProvider/res/values-tl/strings.xml
+++ b/packages/SettingsProvider/res/values-tl/strings.xml
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4567566098528588863">"Imbakan ng Mga Setting"</string>
+    <string name="app_label" msgid="4567566098528588863">"Storage ng Mga Setting"</string>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 175b424..6a05af1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2236,10 +2236,7 @@
                     ringerModeAffectedStreams);
 
             loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
-                    ((1 << AudioManager.STREAM_MUSIC) |
-                     (1 << AudioManager.STREAM_RING) |
-                     (1 << AudioManager.STREAM_NOTIFICATION) |
-                     (1 << AudioManager.STREAM_SYSTEM)));
+                    AudioService.DEFAULT_MUTE_STREAMS_AFFECTED);
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index 2e95b4e..ed0697e 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -21,5 +21,5 @@
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Hata raporunuzu paylaşmak için hızlıca sola kaydırın"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hata raporunuzu paylaşmak için dokunun"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Hata raporları, kişisel ve özel bilgiler dahil olmak üzere sistemin çeşitli günlük dosyalarından veriler içerir. Hata raporlarını sadece güvendiğiniz uygulamalar ve kişilerle paylaşın."</string>
-    <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu mesajı göster"</string>
+    <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu iletiyi göster"</string>
 </resources>
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2ff9a28..47ef42a 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -20,6 +20,8 @@
     $(LOCAL_PATH)/res
 LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
 
+include frameworks/base/packages/SettingsLib/common.mk
+
 include $(BUILD_PACKAGE)
 
 ifeq ($(EXCLUDE_SYSTEMUI_TESTS),)
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 4cf4f52d..01b2713 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -21,7 +21,8 @@
     android:id="@+id/keyguard_bottom_area"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
-    >
+    android:outlineProvider="none"
+    android:elevation="5dp" > <!-- Put it above the status bar header -->
 
     <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
         android:id="@+id/keyguard_indication_text"
diff --git a/packages/SystemUI/res/layout/notification_public_default.xml b/packages/SystemUI/res/layout/notification_public_default.xml
index efabc06..044ba09 100644
--- a/packages/SystemUI/res/layout/notification_public_default.xml
+++ b/packages/SystemUI/res/layout/notification_public_default.xml
@@ -16,12 +16,9 @@
   -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="64dp"
-    internal:layout_minHeight="64dp"
-    internal:layout_maxHeight="64dp"
     >
     <ImageView android:id="@+id/icon"
         android:layout_width="40dp"
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
index ea2e1e1..0ba3ba3 100644
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_detail_item.xml
@@ -39,6 +39,7 @@
             android:id="@android:id/title"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:ellipsize="end"
             android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
 
         <TextView
@@ -58,4 +59,4 @@
         android:scaleType="center"
         android:src="@drawable/ic_qs_cancel" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 8f367a6..26523f9 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -15,7 +15,7 @@
 -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent" 
+    android:layout_width="match_parent"
     android:layout_height="match_parent">
     <!-- Status Bar Scrim View -->
     <ImageView
@@ -29,9 +29,16 @@
     <!-- Recents View -->
     <com.android.systemui.recents.views.RecentsView
         android:id="@+id/recents_view"
-        android:layout_width="match_parent" 
+        android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:focusable="true" />
+        android:focusable="true">
+        <!-- MultiStack Debug View -->
+        <ViewStub android:id="@+id/multistack_debug_view_stub"
+               android:layout="@layout/recents_multistack_debug"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_gravity="left|bottom" />
+    </com.android.systemui.recents.views.RecentsView>
 
     <!-- Empty View -->
     <ViewStub android:id="@+id/empty_view_stub"
diff --git a/packages/SystemUI/res/layout/recents_multistack_debug.xml b/packages/SystemUI/res/layout/recents_multistack_debug.xml
new file mode 100644
index 0000000..6524a54
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_multistack_debug.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="left|bottom"
+    android:orientation="vertical">
+    <Button
+        android:id="@+id/add_stack"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:padding="8dp"
+        android:textSize="20sp"
+        android:textColor="#ffffffff"
+        android:text="@string/recents_multistack_add_stack"
+        android:fontFamily="sans-serif"
+        android:background="#000000"
+        android:alpha="0.5" />
+    <Button
+        android:id="@+id/resize_stack"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:padding="8dp"
+        android:textSize="20sp"
+        android:textColor="#ffffffff"
+        android:text="@string/recents_multistack_resize_stack"
+        android:fontFamily="sans-serif"
+        android:background="#000000"
+        android:alpha="0.5" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
new file mode 100644
index 0000000..36e54a0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
@@ -0,0 +1,70 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:orientation="vertical"
+    android:descendantFocusability="beforeDescendants"
+    android:focusableInTouchMode="true">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <EditText
+            android:id="@+id/inset_left"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:hint="Left"
+            android:singleLine="true"
+            android:imeOptions="actionNext"
+            android:inputType="number"
+            android:selectAllOnFocus="true" />
+        <EditText
+            android:id="@+id/inset_top"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:hint="Top"
+            android:singleLine="true"
+            android:imeOptions="actionNext"
+            android:inputType="number"
+            android:selectAllOnFocus="true" />
+        <EditText
+            android:id="@+id/inset_right"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:hint="Right"
+            android:singleLine="true"
+            android:imeOptions="actionNext"
+            android:inputType="number"
+            android:selectAllOnFocus="true" />
+        <EditText
+            android:id="@+id/inset_bottom"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:hint="Bottom"
+            android:singleLine="true"
+            android:imeOptions="actionDone"
+            android:inputType="number"
+            android:selectAllOnFocus="true" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index f1d8ad0..53047a3 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -43,6 +43,16 @@
         android:ellipsize="marquee"
         android:fadingEdge="horizontal" />
     <com.android.systemui.recents.views.FixedSizeImageView
+        android:id="@+id/move_task"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginEnd="52dp"
+        android:layout_gravity="center_vertical|end"
+        android:padding="12dp"
+        android:background="@drawable/recents_button_bg"
+        android:src="@drawable/star"
+        android:visibility="gone" />
+    <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/dismiss_task"
         android:layout_width="48dp"
         android:layout_height="48dp"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 428c9f4..ba2c8eb 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Kon nie <xliff:g id="APP">%s</xliff:g> begin nie."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Maak alle programme toe"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Gelaai"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Laai tans"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> tot vol"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c18757d..f4ba6f6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> አስወግድ።"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ተሰናብቷል::"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ሁሉም የቅርብ ጊዜ ማመልከቻዎች ተሰናብተዋል።"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> በመጀመር ላይ።"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ማሳወቂያ ተወግዷል።"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"የማሳወቂያ ጥላ።"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ማያ ገጽ መሰካት"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ሁሉንም ማመልከቻዎች አሰናብት"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ባትሪ ሞልቷል"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ኃይል በመሙላት ላይ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 44f4948..67e151e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"بحث"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"تعذر بدء <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"تجاهل كل التطبيقات"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"تم الشحن"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"جارٍ الشحن"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> حتى الاكتمال"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index a011911..4d37f4a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отхвърляне на <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложението <xliff:g id="APP">%s</xliff:g> е отхвърлено."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Всички скорошни приложения са отхвърлени."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> се стартира."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известието е отхвърлено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Падащ панел с известия."</string>
@@ -192,10 +191,10 @@
     <string name="accessibility_quick_settings_close" msgid="3115847794692516306">"Затваряне на панела."</string>
     <string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"Повече време."</string>
     <string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"По-малко време."</string>
-    <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"Светкавицата е изключена."</string>
-    <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"Светкавицата е включена."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"Светкавицата се изключи."</string>
-    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"Светкавицата се включи."</string>
+    <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"Фенерчето е изключено."</string>
+    <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"Фенерчето е включено."</string>
+    <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"Фенерчето е изключено."</string>
+    <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"Фенерчето е включено."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"Функцията за инвертиране на цветовете се изключи."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"Функцията за инвертиране на цветовете се включи."</string>
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"Мобилната точка за достъп се изключи."</string>
@@ -269,7 +268,7 @@
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Тетъринг"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка за достъп"</string>
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Известия"</string>
-    <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Светкавица"</string>
+    <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Фенерче"</string>
     <string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Мобилни данни"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Пренос на данни"</string>
     <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Оставащи данни"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"фиксиране на екрана"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Отхвърляне на всички приложения"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заредена"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарежда се"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 8d33282..71c78b2 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> খারিজ করুন।"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> খারিজ করা হয়েছে৷"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"সমস্ত সাম্প্রতিক অ্যাপ্লিকেশন খারিজ করা হয়েছে।"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> তারাঙ্কিত করা হচ্ছে।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"বিজ্ঞপ্তি খারিজ করা হয়েছে৷"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"বিজ্ঞপ্তি শেড৷"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"স্ক্রীন পিন করা"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"অনুসন্ধান"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> শুরু করা যায়নি৷"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"সমস্ত অ্যাপ্লিকেশন খারিজ করুন"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"চার্জ হয়েছে"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"চার্জ হচ্ছে"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e33c49d..16d5d109 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descarta <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"S\'han descartat totes les aplicacions recents."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"S\'està iniciant <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixació de pantalla"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Descarta totes les aplicacions"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"S\'està carregant"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c8907c7..c949164 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zavřít aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikace <xliff:g id="APP">%s</xliff:g> byla odebrána."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všechny naposledy použité aplikace byly odstraněny."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spouštění aplikace <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Oznámení je zavřeno."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel oznámení."</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"připnutí obrazovky"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Odstranit všechny aplikace"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabito"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíjení"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index f442472..733f0c9 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Afvis <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> er annulleret."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle de seneste applikationer er lukket."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> startes."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Underretningen er annulleret."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Underretningspanel."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"bliv i app"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> kunne ikke startes."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Luk alle applikationer"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opladet"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Oplader"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index f3349d2..9d0b907 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> beenden"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> entfernt"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle kürzlich verwendeten Apps wurden entfernt."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> wird gestartet."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Benachrichtigung geschlossen"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Benachrichtigungsleiste"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Bildschirmfixierung"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Alle Apps entfernen"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Aufgeladen"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Wird aufgeladen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 04c4fe8..5e672b3 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Παράβλεψη <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Απορρίφθηκαν <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Έγινε παράβλεψη όλων των πρόσφατων εφαρμογών."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Έναρξη <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Η ειδοποίηση έχει απορριφθεί."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Πλαίσιο σκίασης ειδοποιήσεων."</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"καρφίτσωμα οθόνης"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Δεν ήταν δυνατή η εκκίνηση της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Παράβλεψη όλων των εφαρμογών"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Φορτίστηκε"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Φόρτιση"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 8e46cce..0d627cf 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Dismiss all applications"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 8e46cce..0d627cf 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Dismiss all applications"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 7cf20ba..a70ef9a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rechazar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartada."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se descartaron todas las aplicaciones recientes."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pantalla de notificaciones"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Fijar pantalla"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Descartar todas las aplicaciones"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 82fe2bd..54926ce 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Se ha eliminado <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se han ignorado todas las aplicaciones recientes."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pantalla de notificaciones"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fijación de pantalla"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ignorar todas las aplicaciones"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index f8ce386..cb2dd7b 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rakendusest <xliff:g id="APP">%s</xliff:g> loobumine."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Loobusite rakendusest <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kõikidest hiljutistest rakendustest on loobutud"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Rakenduse <xliff:g id="APP">%s</xliff:g> käivitamine."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Märguandest on loobutud."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Märguande vari."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekraanikuva kinnitamine"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Loobu kõikidest rakendustest"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laetud"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Laadimine"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index b456bee..ae02787 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Baztertu <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> baztertu da."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Azken aplikazio guztiak baztertu da."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> hasten."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Jakinarazpena baztertu da."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Jakinarazpenen panela."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pantaila-ainguratzea"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"bilatu"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Ezin izan da hasi <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Baztertu aplikazio guztiak"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kargatuta"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Kargatzen"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index c50eabf..becc464 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> شروع نشد."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"رد کردن همه برنامه‌ها"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"شارژ کامل شد"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"در حال شارژ شدن"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> مانده تا شارژ کامل شود"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index b8b2d65..5fbe650 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hylätään <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> hylättiin."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kaikki viimeisimmät sovellukset on hylätty."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Käynnistetään <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ilmoitus hylätty."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Ilmoitusalue."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"näytön kiinnitys"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Sovelluksen <xliff:g id="APP">%s</xliff:g> käynnistäminen epäonnistui."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Hylkää kaikki sovellukset"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ladattu"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Ladataan"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 156e4a7..daf87ef 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Volet des notifications"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Supprimer toutes les applications"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargée"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charge en cours..."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 31ef4ef..32dac57 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Volet des notifications"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Supprimer toutes les applications"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargé"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"En charge"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 4c40a5a..8b1a3da 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rexeitar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Rexeitouse <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Rexeitáronse todas as aplicacións recentes."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación rexeitada"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Sombra de notificación"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixación de pantalla"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Non foi posible iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Rexeitar todas as aplicacións"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 50ef47a..34040d1 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> को ख़ारिज करें."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खा़रिज कर दिया गया."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"हाल ही के सभी ऐप्लिकेशन ख़ारिज कर दिए गए."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ हो रहा है."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"नोटिफिकेशन खारिज की गई."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"नोटिफिकेशन शेड."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रीन पिन करना"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ नहीं किया जा सका."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"सभी ऐप्लिकेशन ख़ारिज करें"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज हो गई है"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हो रही है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 3fb20a9..5fc2f88 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacivanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> odbačena je."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Odbačene su sve nedavne aplikacije."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obavijest je odbačena."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Zaslon obavijesti."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"prikvačivanje zaslona"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Odbaci sve aplikacije"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 8787599..8b0edbc 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"A(z) <xliff:g id="APP">%s</xliff:g> elvetése."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eltávolítva."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Az összes alkalmazás eltávolítva a nemrég használtak közül."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A(z) <xliff:g id="APP">%s</xliff:g> indítása."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Értesítés elvetve."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Értesítési felület."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"képernyő rögzítése"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Összes alkalmazás elvetése"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Feltöltve"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Töltés"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index c0f8776..2fa89c8 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Անտեսել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>-ը անտեսված է:"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Բոլոր վերջին հավելվածները հեռացվել են ցուցակից:"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Մեկնարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ծանուցումը անտեսվեց:"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Ծանուցումների վահանակ:"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"էկրանի ամրակցում"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Հնարավոր չէ գործարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Հեռացնել բոլոր հավելվածները"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Լիցքավորված է"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Լիցքավորվում է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 025bdbe..501b99e 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Menyingkirkan <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> disingkirkan."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaru telah ditutup."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulai <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pemberitahuan disingkirkan."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bayangan pemberitahuan."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pin ke layar"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Tutup semua aplikasi"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Terisi"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengisi daya"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 87fb619..a408204 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hunsa <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> vísað frá."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Öll nýleg forrit fjarlægð."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Ræsir <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Tilkynningu lokað."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Tilkynningasvæði."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"skjáfesting"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"leita"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Fjarlægja öll forrit"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Fullhlaðin"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Í hleðslu"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 8144bc0..369e51a 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Elimina <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eliminata."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tutte le applicazioni recenti sono state rimosse."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Avvio di <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifica eliminata."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Area notifiche."</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"blocco su schermo"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Rimuovi tutte le applicazioni"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carica"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"In carica"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 15bd235..bdfdcc3 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"חפש"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"לא ניתן היה להפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"סגור את כל האפליקציות"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"טעון"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"טוען"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> עד למילוי"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 0183e21..330e55a 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>は削除されました。"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近のアプリケーションをすべて消去しました。"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>を開始しています。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知が削除されました。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知シェード"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"画面固定"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>を開始できません。"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"すべてのアプリケーションを消去"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"充電が完了しました"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"充電しています"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 42fdfe9..26b4025 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-ის უგულებელყოფა."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ამოშლილია სიიდან."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ყველა ბოლო აპლიკაცია გაუქმდა."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> იწყება."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"შეტყობინება წაიშალა."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"შეტყობინებების ფარდა"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ეკრანზე ჩამაგრება"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-ის გამოძახება ვერ მოხერხდა."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ყველა აპლიკაციის გაუქმება"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"დატენილია"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"მიმდინარეობს დატენვა"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 7190aa2..6258a8c 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> қолданбасынан бас тарту."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> алынып тасталған."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Барлық жақындағы қабылданбаған қолданбалар."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> іске қосылуда."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Хабар алынып тасталды."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Хабарландыру тақтасы"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экранды бекіту"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"іздеу"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> іске қосу мүмкін болмады."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Барлық қолданбаларды қабылдамау"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зарядталды"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядталуда"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index e88deb7..71d616f 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"បោះបង់ <xliff:g id="APP">%s</xliff:g> ។"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"កម្មវិធីថ្មីៗទាំងអស់ត្រូវបានបោះបង់។"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ។"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"បាន​បដិសេធ​ការ​ជូនដំណឹង"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ពណ៌​ការ​ជូន​ដំណឹង"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ការ​ភ្ជាប់​អេក្រង់"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ទេ។"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"បោះបង់កម្មវិធីទាំងអស់"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បាន​បញ្ចូល​ថ្ម​​"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"កំពុង​បញ្ចូល​ថ្ម"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 5b7738b..b2e2a1a 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸು."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ಇತ್ತೀಚಿನ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ಅಧಿಸೂಚನೆ ವಜಾಗೊಂಡಿದೆ."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ಅಧಿಸೂಚನೆಯ ಛಾಯೆ."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ಸ್ಕ್ರೀನ್ ಪಿನ್ನಿಂಗ್"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ಹುಡುಕಾಟ"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಿಲ್ಲ."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 906ba2b..a0d614a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>을(를) 숨깁니다."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>이(가) 제거되었습니다."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"최근 사용한 애플리케이션을 모두 닫았습니다."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>을(를) 시작하는 중입니다."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"알림이 제거되었습니다."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"알림 세부정보"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"화면 고정"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"모든 애플리케이션 닫기"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"충전됨"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"충전 중"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 30e5c97..f51814f 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -184,8 +184,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> этибарга албоо."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> жок болду."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Акыркы колдонмолордун баары көз жаздымда калтырылды."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> иштеп баштоодо."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Эскертме жок кылынды."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Эскертмелер көшөгөсү."</string>
@@ -307,7 +306,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экран кадоо"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"издөө"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> баштай алган жок."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Бардык колдонмолорду көз жаздымда калтыруу"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Кубатталды"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Кубатталууда"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 33e98b2..01ef31a 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ຊອກຫາ"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"ບໍ່​ສາ​ມາດ​ເລີ່ມ <xliff:g id="APP">%s</xliff:g> ໄດ້."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ປ່ອຍ​ທຸກ​ແອັບ​ພ​ລິ​ເຄ"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ສາກເຕັມແລ້ວ."</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ກຳລັງສາກໄຟ"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ຈຶ່ງ​ຈະ​ເຕັມ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 49f38b9..50fdaff 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Atsisakyti <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Atsisakyta programos „<xliff:g id="APP">%s</xliff:g>“."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Atsisakyta visų naujausių programų."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Paleidžiama <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pranešimo atsisakyta."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pranešimų gaubtas."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekrano prisegimas"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Nepavyko paleisti <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Atsisakyti visų programų"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Įkrautas"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Kraunamas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a0f7b20..4d36ab5 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Nerādīt lietotni <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Lietotne <xliff:g id="APP">%s</xliff:g> vairs netiek rādīta."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Visas nesen izmantotās lietojumprogrammas tika noņemtas."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Notiek lietotnes <xliff:g id="APP">%s</xliff:g> palaišana."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Paziņojums netiek rādīts."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Paziņojumu panelis"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Piespraust ekrānu"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Noņemt visas lietojumprogrammas"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulators uzlādēts"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Notiek uzlāde"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 67314c0..a026e6f 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отфрли <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> е отфрлена."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Сите неодамнешни апликации се отфрлени."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Се стартува <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известувањето е отфрлено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панел за известување"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"прикачување екран"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"пребарај"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не може да се вклучи."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Отфрли ги сите апликации"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Наполнета"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Се полни"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 41e5ef8..d2b2247 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> നിരസിക്കുക."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> നിരസിച്ചു."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"അടുത്തിടെയുള്ള എല്ലാ അപ്ലിക്കേഷനും നിരസിച്ചു."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കുന്നു."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"അറിയിപ്പ് നിരസിച്ചു."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"അറിയിപ്പ് ഷെയ്‌ഡ്."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"സ്ക്രീൻ പിൻ ചെയ്യൽ"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"തിരയുക"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കാനായില്ല."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"എല്ലാ അപ്ലിക്കേഷനുകളും നിരസിക്കുക"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ചാർജ്ജുചെയ്‌തു"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ചാർജ്ജുചെയ്യുന്നു"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index ea80976..4982f10 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-г хаах."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> байхгүй."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Хамгийн сүүлийн бүх програмыг арилгасан байна."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж байна."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Мэдэгдэл хаагдсан."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Мэдэгдлийн хураангуй самбар"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"дэлгэц тогтоох"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Бүх програмыг арилгах"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Цэнэглэгдсэн"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Цэнэглэж байна"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index e8f3a98..900890c 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> डिसमिस करा."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> डिसमिस केला."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"अलीकडील सर्व अनुप्रयोग डिसमिस झाले."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करीत आहे."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना डिसमिस केल्या."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना शेड."</string>
@@ -226,7 +225,7 @@
     <string name="start_dreams" msgid="7219575858348719790">"डेड्रीम"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"इथरनेट"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"विमान मोड"</string>
-    <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"ब"</string>
+    <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"ब्लूटुथ"</string>
     <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"ब (<xliff:g id="NUMBER">%d</xliff:g> डिव्हाइसेस)"</string>
     <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"ब बंद"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"कोणतेही जोडलेले डिव्हाइसेस उपलब्ध नाहीत"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्‍क्रीन पिन करणे"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"शोधा"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करणे शक्य झाले नाही."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"सर्व अनुप्रयोग डिसमिस करा"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज झाली"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज होत आहे"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 7b89c1c..f3c6e32 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ketepikan <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ditolak."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaharu diketepikan."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulakan <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pemberitahuan diketepikan."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bidai pemberitahuan."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"penyematan skrin"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ketepikan semua aplikasi"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Sudah dicas"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengecas"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 86e4729..c9db1b1 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ကို ပယ်လိုက်ရန်"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ထုတ်ထားသည်။"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"မကြာသေးမီက အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်ပြီးပါပြီ။"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ကို စတင်နေသည်။"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"အကြောင်းကြားချက်ကိုဖယ်ရှားပြီး"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"အ​ကြောင်းကြားစာအကွက်"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"မျက်နှာပြင် ပင်ထိုးမှု"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ရှာဖွေရန်"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ကို မစနိုင်ပါ။"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်မည်"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"အားသွင်းပြီး"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"အားသွင်းနေ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7ac8a69..b2fbd78 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Avvis <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> avvist."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle nylig brukte apper er avvist."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starter <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Varselet ble skjult."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Varselskygge."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"én-appsmodus"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Avvis alle apper"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Oppladet"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Lader"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index d1d19af..67a1c30 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> खारेज गर्नुहोस्।"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खारेज गरिएको छ।"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"सबै हालका अनुप्रयोगहरू खारेज गरियो।"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>सुरु गर्दै।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना खारेज।"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना कक्ष।"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रिन पिन गर्दै"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"खोजी गर्नुहोस्"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"सुरु गर्न सकिएन <xliff:g id="APP">%s</xliff:g>।"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"सबै अनुप्रयोगहरू खारेज गर्नुहोस्"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज भयो"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हुँदै"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 451ff9a..2c2d6ef 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> sluiten."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> verwijderd."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle recente apps gesloten."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> starten."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Melding verwijderd."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Meldingenpaneel."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"scherm vastzetten"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Alle apps sluiten"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opgeladen"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Opladen"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 81e22ca..e2ef340 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Usuń stąd <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>: zamknięto."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Wszystkie ostatnie aplikacje zostały zamknięte."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Uruchamiam <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Zamknięto powiadomienie."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Obszar powiadomień."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"przypinanie ekranu"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Zamknij wszystkie aplikacje"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Naładowana"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Ładowanie"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ae7b551..df1192d 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ignorado."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todas as aplicações recentes foram ignoradas."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A iniciar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação ignorada."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Painel de notificações."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação no ecrã"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar o <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ignorar todas as aplicações"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"A carregar"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 7887e06..770cabe 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartado."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todos os apps recentes foram dispensados."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação dispensada."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Aba de notificações."</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação de tela"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Dispensar todos os apps"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Carregando"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 0c1b4ec..cd3a1c0 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Închideți <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> a fost eliminată."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toate aplicațiile recente au fost închise."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Se inițiază <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificarea a fost închisă."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Fereastră pentru notificări."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixare pe ecran"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Închideți toate aplicațiile"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"S-a încărcat"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Se încarcă"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 8425cb4..7f5d352 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -19,7 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="7164937344850004466">"Графический интерфейс системы"</string>
+    <string name="app_label" msgid="7164937344850004466">"Интерфейс системы"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string>
     <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Удалить из списка"</string>
     <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"О приложении"</string>
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Удаление приложения <xliff:g id="APP">%s</xliff:g> из списка."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" удалено из списка."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Все недавние приложения закрыты."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск приложения <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Уведомление закрыто"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панель уведомлений"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Заблокировать в приложении"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\""</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Закрыть все приложения"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Батарея заряжена"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядка батареи"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 760e49d..5bd1f8d 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ඉවතලන්න."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> අස් කර ඇත."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"සියලුම මෑත යෙඳුම් අස් කරන ලදි."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කරමින්."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"දැනුම්දීම නිෂ්ප්‍රභා කරඇත."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"දැනුම්දීම් ආවරණය."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"තිර ඇමිණීම"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"සෙවීම"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කළ නොහැක."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"සියලුම යෙදුම් අස් කරන්න"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"අරෝපිතයි"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ආරෝපණය වෙමින්"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b20d8f9..5183dab 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zrušiť aplikáciu <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikácia <xliff:g id="APP">%s</xliff:g> bola zrušená."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všetky nedávne aplikácie boli odmietnuté."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spúšťa sa aplikácia <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Upozornenie bolo zrušené."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel upozornení."</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripnutie k obrazovke"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Odmietnuť všetky aplikácie"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabitá"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíja sa"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 1981bfd..2afb65b 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Opusti aplikacijo <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je bila odstranjena."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Vse nedavne aplikacije so bile opuščene."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Zaganjanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obvestilo je bilo odstranjeno."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Zaslon z obvestili."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripenjanje zaslona"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Opusti vse aplikacije"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulator napolnjen"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Polnjenje"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index fc4dd64..d7ab3a6 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Одбаците <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Апликација <xliff:g id="APP">%s</xliff:g> је одбачена."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Све недавно коришћене апликације су одбачене."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Покрећемо <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Обавештење је одбачено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Прозор са обавештењима."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"качење екрана"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Одбаци све апликације"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Напуњена је"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Пуњење"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6cc5ad0..f293649 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ta bort <xliff:g id="APP">%s</xliff:g> från listan."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> togs bort permanent."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alla appar har tagits bort från listan Senaste."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Startar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Meddelandet ignorerades."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Meddelandepanel."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fästa skärmen"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ta bort alla appar"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laddat"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Laddar"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 3b539b9..d7621e9 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ondoa <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> imeondolewa."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Programu za hivi majuzi zimeondolewa."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Inaanzisha <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Arifa imetupwa."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Kivuli cha arifa."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"kudumisha programu moja"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Haikuweza kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ondoa programu zote"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Betri imejaa"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Inachaji"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index bf163f6..d861522 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ஐ நிராகரி."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> விலக்கப்பட்டது."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"எல்லா சமீபத்திய பயன்பாடுகளும் விலக்கப்பட்டன."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ஐத் தொடங்குகிறது."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"அறிவிப்பு நிராகரிக்கப்பட்டது."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"அறிவிப்பு விவரம்."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"திரையை பின் செய்தல்"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"தேடு"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ஐத் தொடங்க முடியவில்லை."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"எல்லா பயன்பாடுகளையும் விலக்கு"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"சார்ஜ் செய்யப்பட்டது"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"சார்ஜாகிறது"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 04616c9..10b3214 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ని తీసివేయండి."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> తీసివేయబడింది."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"అన్ని ఇటీవలి అనువర్తనాలు తీసివేయబడ్డాయి."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభిస్తోంది."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"నోటిఫికేషన్ తీసివేయబడింది."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"నోటిఫికేషన్ షేడ్."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"స్క్రీన్ పిన్నింగ్"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"శోధించు"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"అన్ని అనువర్తనాలను తీసివేయి"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ఛార్జ్ చేయబడింది"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ఛార్జ్ అవుతోంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5b51a87..1bc9585 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"ยกเลิก <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ถูกลบไปแล้ว"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ปิดแอปพลิเคชันล่าสุดทั้งหมดแล้ว"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"กำลังเริ่มต้น <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ปิดการแจ้งเตือนแล้ว"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"หน้าต่างแจ้งเตือน"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"การตรึงหน้าจอ"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"ไม่สามารถเริ่มใช้ <xliff:g id="APP">%s</xliff:g>"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ปิดแอปพลิเคชันทั้งหมด"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ชาร์จแล้ว"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"กำลังชาร์จ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 2367762..cee541e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"I-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Hindi pinansin ang <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Na-dismiss ang lahat ng kamakailang application."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Sinisimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Na-dismiss ang notification."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
@@ -282,8 +281,13 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pagpi-pin sa screen"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Hindi masimulan <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
-    <skip />
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"I-dismiss ang lahat ng application"</string>
+    <string name="recents_multistack_add_stack" msgid="5044995965068125420">"+"</string>
+    <string name="recents_multistack_remove_stack" msgid="3014058144068028841">"-"</string>
+    <string name="recents_multistack_resize_stack" msgid="5511174284568497822">"[]"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Custom"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nasingil na"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Nagcha-charge"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> hanggang mapuno"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 2b7f059..21f1472 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -144,7 +144,7 @@
     <string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
     <string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Kablosuz"</string>
     <string name="accessibility_no_sim" msgid="8274017118472455155">"SIM kart yok."</string>
-    <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Bluetooth İnternet paylaşımı"</string>
+    <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Bluetooth tethering"</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"Uçak modu."</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Pil yüzdesi: <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistem ayarları."</string>
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> uygulamasını kapat."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> kaldırıldı."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tüm son uygulamalar kapatıldı."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> başlatılıyor."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Bildirim kapatıldı."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bildirim gölgesi."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekran sabitleme"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Tüm uygulamaları kapat"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ödeme alındı"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Şarj oluyor"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 1f086ff..0909eaa 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Видалити додаток <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Програму <xliff:g id="APP">%s</xliff:g> закрито."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Усі останні додатки закрито."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск додатка <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Сповіщення відхилено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панель сповіщень."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"закріпити екран"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Не вдалося запустити <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Закрити всі додатки"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заряджено"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Заряджається"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index cb6f3f0..1b29477 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> کو مسترد کریں۔"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> کو ہٹا دیا گیا۔"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"سبھی حالیہ ایپلیکیشنز کو برخاست کر دیا گیا۔"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> شروع ہو رہی ہے۔"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"اطلاع مسترد ہوگئی۔"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"اطلاعاتی شیڈ۔"</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"اسکرین کو پن کرنا"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"تلاش کریں"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"سبھی ایپلیکیشنز کو برخاست کریں"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"چارج ہوگئی"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"چارج ہو رہی ہے"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 58bf477..15c84cb 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Olib tashlash: <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> olib tashlangan."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Yaqinda ishlatilgan barcha ilovalar olib tashlandi."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ishga tushirilmoqda."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Xabarnoma e‘tiborsiz qoldirildi."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Xabarnoma soyasi."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekranni qadab qo‘yish"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"qidirish"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"“<xliff:g id="APP">%s</xliff:g>” ilovasini ishga tushirib bo‘lmadi."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Barcha ilovalarni olib tashlash"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Batareya quvvati to‘ldi"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Quvvat olmoqda"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c34d0ed..20a19a3 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -159,8 +159,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Xóa bỏ <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> đã bị loại bỏ."</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Đã bỏ qua tất cả các ứng dụng gần đây."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Bắt đầu <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Đã loại bỏ thông báo."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bóng thông báo."</string>
@@ -282,7 +281,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"khóa màn hình"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Bỏ qua tất cả các ứng dụng"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Đã sạc"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Đang sạc"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 49fe785..0c5c57b 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -143,7 +143,7 @@
     <string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"漫游中"</string>
     <string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
     <string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"WLAN"</string>
-    <string name="accessibility_no_sim" msgid="8274017118472455155">"无SIM卡。"</string>
+    <string name="accessibility_no_sim" msgid="8274017118472455155">"无 SIM 卡。"</string>
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"蓝牙网络共享。"</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"飞行模式。"</string>
     <!-- String.format failed for translation -->
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"移除<xliff:g id="APP">%s</xliff:g>。"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"已删除<xliff:g id="APP">%s</xliff:g>"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"已关闭所有最近用过的应用。"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在启动<xliff:g id="APP">%s</xliff:g>。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"已关闭通知。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知栏。"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"固定屏幕"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"关闭所有应用"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"充电完成"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"正在充电"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 22e0ed1..6e6cc1e 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"所有最近使用的應用程式均已關閉。"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知已關閉。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知欄。"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"螢幕固定"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"關閉所有應用程式"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已完成充電"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 674b974..5329977 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -161,8 +161,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近使用的應用程式已全部關閉。"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"已關閉通知。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知欄。"</string>
@@ -284,7 +283,18 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"螢幕固定"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
-    <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
+    <string name="recents_dismiss_all_message" msgid="8495275386693095768">"關閉所有應用程式"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
     <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充飽"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 19e6b49..2a7adb5 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -282,6 +282,18 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Ayikwazanga ukuqala i-<xliff:g id="APP">%s</xliff:g>."</string>
     <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Cashisa zonke izinhlelo zokusebenza"</string>
+    <!-- no translation found for recents_multistack_add_stack (5044995965068125420) -->
+    <skip />
+    <!-- no translation found for recents_multistack_remove_stack (3014058144068028841) -->
+    <skip />
+    <!-- no translation found for recents_multistack_resize_stack (5511174284568497822) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
+    <skip />
+    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
+    <skip />
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kushajiwe"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Iyashaja"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ize igcwale"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7783fc..4c0cea8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -30,7 +30,7 @@
     <color name="notification_list_shadow_top">#80000000</color>
     <drawable name="recents_callout_line">#99ffffff</drawable>
     <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
-    <color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
+    <color name="batterymeter_frame_color">#4DFFFFFF</color><!-- 30% white -->
     <color name="batterymeter_charge_color">#FFFFFFFF</color>
     <color name="batterymeter_bolt_color">#FFFFFFFF</color>
     <color name="qs_batterymeter_frame_color">#FF404040</color>
@@ -129,4 +129,5 @@
     <color name="segmented_button_selected">#FFFFFFFF</color>
     <color name="segmented_button_unselected">#B3B0BEC5</color><!-- 70% blue grey 200 -->
     <color name="volume_panel_divider">#1FFFFFFF</color><!-- 12% white -->
+    <color name="light_mode_icon_color">#FF616161</color><!-- grey 700 -->
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 071b9a3..2659009 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -250,7 +250,7 @@
     <integer name="doze_pickup_vibration_threshold">2000</integer>
 
     <!-- Doze: can we assume the pickup sensor includes a proximity check? -->
-    <bool name="doze_pickup_performs_proximity_check">true</bool>
+    <bool name="doze_pickup_performs_proximity_check">false</bool>
 
     <!-- Doze: pulse parameter - how long does it take to fade in? -->
     <integer name="doze_pulse_duration_in">900</integer>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 68dc269..aa53a3e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -40,5 +40,11 @@
     <item type="id" name="notification_power"/>
     <item type="id" name="notification_screenshot"/>
     <item type="id" name="notification_hidden"/>
+
+    <!-- Whether the icon is from a notification for which targetSdk < L -->
+    <item type="id" name="icon_is_pre_L"/>
+
+    <!-- For notification icons for which targetSdk < L, this caches whether the icon is grayscale -->
+    <item type="id" name="icon_is_grayscale" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c977db9..6afca8a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -630,10 +630,6 @@
     <string name="quick_settings_done">Done</string>
     <!-- QuickSettings: Control panel: Label for connected device. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_connected">Connected</string>
-    <!-- QuickSettings: Control panel: Label for a connected Wi-Fi access point when the connection is established by a Wi-Fi assistant application. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_connected_via_wfa">Connected via Wi\u2011Fi assistant</string>
-    <!-- QuickSettings: Control panel: Label for saved device or connection. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_saved">Saved</string>
     <!-- QuickSettings: Control panel: Label for connecting device. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_connecting">Connecting...</string>
     <!-- QuickSettings: Tethering. [CHAR LIMIT=NONE] -->
@@ -672,6 +668,19 @@
     <!-- Recents: Dismiss all button. [CHAR LIMIT=NONE] -->
     <string name="recents_dismiss_all_message">Dismiss all applications</string>
 
+    <!-- Recents: MultiStack add stack button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack">+</string>
+    <!-- Recents: MultiStack remove stack button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_remove_stack">-</string>
+    <!-- Recents: MultiStack resize stack button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_resize_stack">[]</string>
+    <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
+    <!-- Recents: MultiStack add stack split vertical radio button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
+    <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
+
     <!-- Expanded Status Bar Header: Battery Charged [CHAR LIMIT=40] -->
     <string name="expanded_header_battery_charged">Charged</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f2b4a69..bf19b8d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,7 +20,7 @@
         <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
     </style>
 
-    <style name="RecentsTheme" parent="@android:style/Theme">
+    <style name="RecentsTheme" parent="@android:style/Theme.Material.Light">
         <!-- NoTitle -->
         <item name="android:windowNoTitle">true</item>
         <!-- Misc -->
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 7bdbd0a..f2f087f 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -23,8 +23,12 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.RectF;
 import android.graphics.Typeface;
 import android.os.BatteryManager;
@@ -56,12 +60,13 @@
     private float mSubpixelSmoothingRight;
     private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
     private float mTextHeight, mWarningTextHeight;
+    private int mIconTint = Color.WHITE;
 
     private int mHeight;
     private int mWidth;
     private String mWarningString;
     private final int mCriticalLevel;
-    private final int mChargeColor;
+    private int mChargeColor;
     private final float[] mBoltPoints;
     private final Path mBoltPath = new Path();
 
@@ -292,11 +297,27 @@
         for (int i=0; i<mColors.length; i+=2) {
             thresh = mColors[i];
             color = mColors[i+1];
-            if (percent <= thresh) return color;
+            if (percent <= thresh) {
+
+                // Respect tinting for "normal" level
+                if (i == mColors.length-2) {
+                    return mIconTint;
+                } else {
+                    return color;
+                }
+            }
         }
         return color;
     }
 
+    public void setIconTint(int tint) {
+        mIconTint = tint;
+        mFramePaint.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_ATOP));
+        mBoltPaint.setColor(tint);
+        mChargeColor = tint;
+        invalidate();
+    }
+
     @Override
     public void draw(Canvas c) {
         BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6df78ac..f3d214f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -489,6 +489,11 @@
         }
 
         @Override
+        public void resetKeyguard() {
+            resetStateLocked();
+        }
+
+        @Override
         public void playTrustedSound() {
             KeyguardViewMediator.this.playTrustedSound();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index 9155102..95ac558 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -190,6 +190,7 @@
         title.setText(item.line1);
         final TextView summary = (TextView) view.findViewById(android.R.id.summary);
         final boolean twoLines = !TextUtils.isEmpty(item.line2);
+        title.setMaxLines(twoLines ? 1 : 2);
         summary.setVisibility(twoLines ? VISIBLE : GONE);
         summary.setText(twoLines ? item.line2 : null);
         view.setMinimumHeight(mContext.getResources() .getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 974235e..4dacacf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -423,7 +423,9 @@
         }
 
         for (TileRecord record : mRecords) {
-            record.tileView.setDual(record.tile.supportsDualTargets());
+            if (record.tileView.setDual(record.tile.supportsDualTargets())) {
+                record.tileView.handleStateChanged(record.tile.getState());
+            }
             if (record.tileView.getVisibility() == GONE) continue;
             final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
             final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index bb353d5..16ae6b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -172,7 +172,7 @@
         }
     }
 
-    public void setDual(boolean dual) {
+    public boolean setDual(boolean dual) {
         final boolean changed = dual != mDual;
         mDual = dual;
         if (changed) {
@@ -199,6 +199,7 @@
         setFocusable(!dual);
         mDivider.setVisibility(dual ? VISIBLE : GONE);
         postInvalidate();
+        return changed;
     }
 
     private void setRipple(RippleDrawable tileBackground) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index ddb96a2..30f92b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -97,9 +97,7 @@
         state.icon = ResourceIcon.get(iconId);
         state.isOverlayIconWide = cb.isDataTypeIconWide;
         state.autoMirrorDrawable = !cb.noSim;
-        state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiConnected
-                ? cb.dataTypeIconId
-                : 0;
+        state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) ? cb.dataTypeIconId : 0;
         state.filter = iconId != R.drawable.ic_qs_no_sim;
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index bd4dbe6..70746c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles;
 
+import java.util.List;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -24,16 +26,15 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.settingslib.wifi.AccessPoint;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTileView;
 import com.android.systemui.qs.SignalTileView;
-import com.android.systemui.statusbar.phone.QSTileHost;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController.AccessPoint;
 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 
 /** Quick settings tile: Wifi **/
@@ -282,10 +283,10 @@
         }
 
         @Override
-        public void onAccessPointsChanged(final AccessPoint[] accessPoints) {
-            mAccessPoints = accessPoints;
+        public void onAccessPointsChanged(final List<AccessPoint> accessPoints) {
+            mAccessPoints = accessPoints.toArray(new AccessPoint[accessPoints.size()]);
             updateItems();
-            if (accessPoints != null && accessPoints.length > 0) {
+            if (accessPoints != null && accessPoints.size() > 0) {
                 fireScanStateChanged(false);
             }
         }
@@ -299,7 +300,7 @@
         public void onDetailItemClick(Item item) {
             if (item == null || item.tag == null) return;
             final AccessPoint ap = (AccessPoint) item.tag;
-            if (!ap.isConnected) {
+            if (!ap.isActive()) {
                 if (mWifiController.connect(ap)) {
                     mHost.collapsePanels();
                 }
@@ -326,16 +327,10 @@
                     final AccessPoint ap = mAccessPoints[i];
                     final Item item = new Item();
                     item.tag = ap;
-                    item.icon = ap.iconId;
-                    item.line1 = ap.ssid;
-                    if (ap.isConnected) {
-                        item.line2 = mContext.getString(ap.isConfigured ?
-                                R.string.quick_settings_connected :
-                                R.string.quick_settings_connected_via_wfa);
-                    } else if (ap.networkId >= 0) {
-                        item.line2 = mContext.getString(R.string.quick_settings_saved);
-                    }
-                    item.overlay = ap.hasSecurity
+                    item.icon = mWifiController.getIcon(ap);
+                    item.line1 = ap.getSsid();
+                    item.line2 = ap.getSummary();
+                    item.overlay = ap.getSecurity() != AccessPoint.SECURITY_NONE
                             ? mContext.getDrawable(R.drawable.qs_ic_wifi_lock)
                             : null;
                     items[i] = item;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index cfd6b40..192acc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -39,6 +39,8 @@
             public static final boolean EnableSearchLayout = true;
             // Enables the thumbnail alpha on the front-most task
             public static final boolean EnableThumbnailAlphaOnFrontmost = false;
+            // Enables all system stacks to show up in the same recents stack
+            public static final boolean EnableMultiStackToSingleStack = true;
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
             // Enables the simulated task affiliations
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 3c75aac..9dd82fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -24,7 +24,6 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -56,7 +55,6 @@
 import com.android.systemui.recents.views.TaskViewTransform;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -112,6 +110,9 @@
 
         /** Preloads the next task */
         public void run() {
+            // Temporarily skip this if multi stack is enabled
+            if (mConfig.multiStackEnabled) return;
+
             RecentsConfiguration config = RecentsConfiguration.getInstance();
             if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
                 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -362,13 +363,21 @@
     }
 
     void showRelativeAffiliatedTask(boolean showNextTask) {
+        // Return early if there is no focused stack
+        int focusedStackId = mSystemServicesProxy.getFocusedStack();
+        TaskStack focusedStack = null;
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
         loader.preloadTasks(plan, true /* isTopTaskHome */);
-        TaskStack stack = plan.getTaskStack();
+        if (mConfig.multiStackEnabled) {
+            if (focusedStackId < 0) return;
+            focusedStack = plan.getTaskStack(focusedStackId);
+        } else {
+            focusedStack = plan.getAllTaskStacks().get(0);
+        }
 
-        // Return early if there are no tasks
-        if (stack.getTaskCount() == 0) return;
+        // Return early if there are no tasks in the focused stack
+        if (focusedStack.getTaskCount() == 0) return;
 
         ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
         // Return early if there is no running task (can't determine affiliated tasks in this case)
@@ -377,7 +386,7 @@
         if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
 
         // Find the task in the recents list
-        ArrayList<Task> tasks = stack.getTasks();
+        ArrayList<Task> tasks = focusedStack.getTasks();
         Task toTask = null;
         ActivityOptions launchOpts = null;
         int taskCount = tasks.size();
@@ -399,7 +408,7 @@
                             R.anim.recents_launch_prev_affiliated_task_source);
                 }
                 if (toTaskKey != null) {
-                    toTask = stack.findTaskWithId(toTaskKey.id);
+                    toTask = focusedStack.findTaskWithId(toTaskKey.id);
                 }
                 numAffiliatedTasks = group.getTaskCount();
                 break;
@@ -473,8 +482,9 @@
             // Reload the widget id before we get the task stack bounds
             reloadSearchBarAppWidget(mContext, mSystemServicesProxy);
         }
-        mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
-                (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), mTaskStackBounds);
+        mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
+                mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+                mTaskStackBounds);
         if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
             mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
         } else {
@@ -653,8 +663,25 @@
             // Create a new load plan if onPreloadRecents() was never triggered
             sInstanceLoadPlan = loader.createLoadPlan(mContext);
         }
+
+        // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
+        // For multi-stack we need to figure out where each of the tasks are going.
+        if (mConfig.multiStackEnabled) {
+            loader.preloadTasks(sInstanceLoadPlan, true);
+            ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
+            TaskStack stack = stacks.get(0);
+            mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
+            TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
+                    mDummyStackView.computeStackVisibilityReport();
+            ActivityOptions opts = getUnknownTransitionActivityOptions();
+            startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
+                    false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+            return;
+        }
+
         loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
-        TaskStack stack = sInstanceLoadPlan.getTaskStack();
+        ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
+        TaskStack stack = stacks.get(0);
 
         // Prepare the dummy stack for the transition
         mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 1833e09..b1ac733 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.ActivityOptions;
+import android.app.Dialog;
 import android.app.SearchManager;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
@@ -43,7 +44,6 @@
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.views.DebugOverlayView;
@@ -75,6 +75,9 @@
     View mEmptyView;
     DebugOverlayView mDebugOverlay;
 
+    // MultiStack debug
+    RecentsMultiStackDialog mMultiStackDebugDialog;
+
     // Search AppWidget
     RecentsAppWidgetHost mAppWidgetHost;
     AppWidgetProviderInfo mSearchAppWidgetInfo;
@@ -190,7 +193,8 @@
         }
 
         // Start loading tasks according to the load plan
-        if (plan.getTaskStack() == null) {
+        ArrayList<TaskStack> stacks = plan.getAllTaskStacks();
+        if (stacks.size() == 0) {
             loader.preloadTasks(plan, mConfig.launchedFromHome);
         }
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
@@ -199,9 +203,7 @@
         loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
         loader.loadTasks(this, plan, loadOpts);
 
-        SpaceNode root = plan.getSpaceNode();
-        ArrayList<TaskStack> stacks = root.getStacks();
-        boolean hasTasks = root.hasTasks();
+        boolean hasTasks = plan.hasTasks();
         if (hasTasks) {
             mRecentsView.setTaskStacks(stacks);
         }
@@ -591,6 +593,40 @@
         }
     }
 
+
+    /**** RecentsMultiStackDialog ****/
+
+    private RecentsMultiStackDialog getMultiStackDebugDialog() {
+        if (mMultiStackDebugDialog == null) {
+            mMultiStackDebugDialog = new RecentsMultiStackDialog(getFragmentManager());
+        }
+        return mMultiStackDebugDialog;
+    }
+
+    @Override
+    public void onMultiStackAddStack() {
+        RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+        dialog.showAddStackDialog();
+    }
+
+    @Override
+    public void onMultiStackResizeStack() {
+        RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+        dialog.showResizeStackDialog();
+    }
+
+    @Override
+    public void onMultiStackRemoveStack() {
+        RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+        dialog.showRemoveStackDialog();
+    }
+
+    @Override
+    public void onMultiStackMoveTask(Task t) {
+        RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+        dialog.showMoveTaskDialog(t);
+    }
+
     /**** RecentsView.RecentsViewCallbacks Implementation ****/
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index bc10a48..1736c77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -134,6 +134,7 @@
     public boolean fakeShadows;
 
     /** Dev options and global settings */
+    public boolean multiStackEnabled;
     public boolean lockToAppEnabled;
     public boolean developerOptionsEnabled;
     public boolean debugModeEnabled;
@@ -294,6 +295,7 @@
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
         lockToAppEnabled = ssp.getSystemSetting(context,
                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
+        multiStackEnabled = "1".equals(ssp.getSystemProperty("overview.enableMultiStack"));
     }
 
     /** Called when the configuration has changed, and we want to reset any configuration specific
@@ -335,8 +337,8 @@
      * Returns the task stack bounds in the current orientation. These bounds do not account for
      * the system insets.
      */
-    public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
-                                   Rect taskStackBounds) {
+    public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
+            int rightInset, Rect taskStackBounds) {
         Rect searchBarBounds = new Rect();
         getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
         if (isLandscape && hasTransposedSearchBar) {
@@ -353,7 +355,7 @@
      * the system insets.
      */
     public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
-                                   Rect searchBarSpaceBounds) {
+            Rect searchBarSpaceBounds) {
         // Return empty rects if search is not enabled
         int searchBarSize = searchBarSpaceHeightPx;
         if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java
new file mode 100644
index 0000000..fdf9d39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.MutableInt;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+
+import java.util.List;
+
+/**
+ * A helper for the dialogs that show when multistack debugging is on.
+ */
+public class RecentsMultiStackDialog extends DialogFragment {
+
+    static final String TAG = "RecentsMultiStackDialog";
+
+    public static final int ADD_STACK_DIALOG = 0;
+    public static final int ADD_STACK_PICK_APP_DIALOG = 1;
+    public static final int REMOVE_STACK_DIALOG = 2;
+    public static final int RESIZE_STACK_DIALOG = 3;
+    public static final int RESIZE_STACK_PICK_STACK_DIALOG = 4;
+    public static final int MOVE_TASK_DIALOG = 5;
+
+    FragmentManager mFragmentManager;
+    int mCurrentDialogType;
+    MutableInt mTargetStackIndex = new MutableInt(0);
+    Task mTaskToMove;
+    SparseArray<ActivityManager.StackInfo> mStacks;
+    List<ResolveInfo> mLauncherActivities;
+    Rect mAddStackRect;
+    Intent mAddStackIntent;
+
+    View mAddStackDialogContent;
+
+    public RecentsMultiStackDialog() {}
+
+    public RecentsMultiStackDialog(FragmentManager mgr) {
+        mFragmentManager = mgr;
+    }
+
+    /** Shows the add-stack dialog. */
+    void showAddStackDialog() {
+        mCurrentDialogType = ADD_STACK_DIALOG;
+        show(mFragmentManager, TAG);
+    }
+
+    /** Creates a new add-stack dialog. */
+    private void createAddStackDialog(final Context context, LayoutInflater inflater,
+            AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+        builder.setTitle("Add Stack - Enter new dimensions");
+        mAddStackDialogContent =
+                inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
+        Rect windowRect = ssp.getWindowRect();
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_left, windowRect.left);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_top, windowRect.top);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_right, windowRect.right);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_bottom, windowRect.bottom);
+        builder.setView(mAddStackDialogContent);
+        builder.setPositiveButton("Add Stack", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                int left = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_left);
+                int top = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_top);
+                int right = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_right);
+                int bottom = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_bottom);
+                if (bottom <= top || right <= left) {
+                    Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
+                    dismiss();
+                    return;
+                }
+
+                // Prompt the user for the app to start
+                dismiss();
+                mCurrentDialogType = ADD_STACK_PICK_APP_DIALOG;
+                mAddStackRect = new Rect(left, top, right, bottom);
+                show(mFragmentManager, TAG);
+            }
+        });
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dismiss();
+            }
+        });
+    }
+
+    /** Creates a new add-stack pick-app dialog. */
+    private void createAddStackPickAppDialog(final Context context, LayoutInflater inflater,
+            AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+        mLauncherActivities = ssp.getLauncherApps();
+        mAddStackIntent = null;
+        int activityCount = mLauncherActivities.size();
+        CharSequence[] activityNames = new CharSequence[activityCount];
+        for (int i = 0; i < activityCount; i++) {
+            activityNames[i] = ssp.getActivityLabel(mLauncherActivities.get(i).activityInfo);
+        }
+        builder.setTitle("Add Stack - Pick starting app");
+        builder.setSingleChoiceItems(activityNames, -1,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        ActivityInfo ai = mLauncherActivities.get(which).activityInfo;
+                        mAddStackIntent = new Intent(Intent.ACTION_MAIN);
+                        mAddStackIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+                        mAddStackIntent.setComponent(new ComponentName(ai.packageName, ai.name));
+                    }
+                });
+        builder.setPositiveButton("Add Stack", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                // Display 0 = default display
+                ssp.createNewStack(0, mAddStackRect, mAddStackIntent);
+            }
+        });
+        builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                // Display 0 = default display
+                ssp.createNewStack(0, mAddStackRect, null);
+            }
+        });
+    }
+
+    /** Shows the resize-stack dialog. */
+    void showResizeStackDialog() {
+        mCurrentDialogType = RESIZE_STACK_PICK_STACK_DIALOG;
+        show(mFragmentManager, TAG);
+    }
+
+    /** Creates a new resize-stack pick-stack dialog. */
+    private void createResizeStackPickStackDialog(final Context context, LayoutInflater inflater,
+            AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+        mStacks = ssp.getAllStackInfos();
+        mTargetStackIndex.value = -1;
+        CharSequence[] stackNames = getAllStacksDescriptions(mStacks, -1, null);
+        builder.setTitle("Resize Stack - Pick stack");
+        builder.setSingleChoiceItems(stackNames, mTargetStackIndex.value,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mTargetStackIndex.value = which;
+                    }
+                });
+        builder.setPositiveButton("Resize Stack", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                if (mTargetStackIndex.value != -1) {
+                    // Prompt the user for the new dimensions
+                    dismiss();
+                    mCurrentDialogType = RESIZE_STACK_DIALOG;
+                    show(mFragmentManager, TAG);
+                }
+            }
+        });
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dismiss();
+            }
+        });
+    }
+
+    /** Creates a new resize-stack dialog. */
+    private void createResizeStackDialog(final Context context, LayoutInflater inflater,
+            AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+        builder.setTitle("Resize Stack - Enter new dimensions");
+        final ActivityManager.StackInfo stack = mStacks.valueAt(mTargetStackIndex.value);
+        mAddStackDialogContent =
+                inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_left, stack.bounds.left);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_top, stack.bounds.top);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_right, stack.bounds.right);
+        setDimensionInEditText(mAddStackDialogContent, R.id.inset_bottom, stack.bounds.bottom);
+        builder.setView(mAddStackDialogContent);
+        builder.setPositiveButton("Resize Stack", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                int left = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_left);
+                int top = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_top);
+                int right = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_right);
+                int bottom = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_bottom);
+                if (bottom <= top || right <= left) {
+                    Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
+                    dismiss();
+                    return;
+                }
+                ssp.resizeStack(stack.stackId, new Rect(left, top, right, bottom));
+            }
+        });
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dismiss();
+            }
+        });
+    }
+
+    /** Shows the remove-stack dialog. */
+    void showRemoveStackDialog() {
+        mCurrentDialogType = REMOVE_STACK_DIALOG;
+        show(mFragmentManager, TAG);
+    }
+
+    /** Shows the move-task dialog. */
+    void showMoveTaskDialog(Task task) {
+        mCurrentDialogType = MOVE_TASK_DIALOG;
+        mTaskToMove = task;
+        show(mFragmentManager, TAG);
+    }
+
+    /** Creates a new move-stack dialog. */
+    private void createMoveTaskDialog(final Context context, LayoutInflater inflater,
+                                AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+        mStacks = ssp.getAllStackInfos();
+        mTargetStackIndex.value = -1;
+        CharSequence[] stackNames = getAllStacksDescriptions(mStacks, mTaskToMove.key.stackId,
+                mTargetStackIndex);
+        builder.setTitle("Move Task to Stack");
+        builder.setSingleChoiceItems(stackNames, mTargetStackIndex.value,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mTargetStackIndex.value = which;
+                    }
+                });
+        builder.setPositiveButton("Move Task", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                if (mTargetStackIndex.value != -1) {
+                    ActivityManager.StackInfo toStack = mStacks.valueAt(mTargetStackIndex.value);
+                    if (toStack.stackId != mTaskToMove.key.stackId) {
+                        ssp.moveTaskToStack(mTaskToMove.key.id, toStack.stackId, true);
+                        mTaskToMove.setStackId(toStack.stackId);
+                    }
+                }
+            }
+        });
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dismiss();
+            }
+        });
+    }
+
+    /** Helper to get an integer value from an edit text. */
+    private int getDimensionFromEditText(View container, int id) {
+        String text = ((EditText) container.findViewById(id)).getText().toString();
+        if (text.trim().length() != 0) {
+            return Integer.parseInt(text.trim());
+        }
+        return 0;
+    }
+
+    /** Helper to set an integer value to an edit text. */
+    private void setDimensionInEditText(View container, int id, int value) {
+        ((EditText) container.findViewById(id)).setText("" + value);
+    }
+
+    /** Gets a list of all the stacks. */
+    private CharSequence[] getAllStacksDescriptions(SparseArray<ActivityManager.StackInfo> stacks,
+            int targetStackId, MutableInt indexOfTargetStackId) {
+        int stackCount = stacks.size();
+        CharSequence[] stackNames = new CharSequence[stackCount];
+        for (int i = 0; i < stackCount; i++) {
+            ActivityManager.StackInfo stack = stacks.valueAt(i);
+            Rect b = stack.bounds;
+            String desc = "Stack " + stack.stackId + " / " +
+                    "" + (stack.taskIds.length > 0 ? stack.taskIds.length : "No") + " tasks\n" +
+                    "(" + b.left + ", " + b.top + ")-(" + b.right + ", " + b.bottom + ")\n";
+            stackNames[i] = desc;
+            if (targetStackId != -1 && stack.stackId == targetStackId) {
+                indexOfTargetStackId.value = i;
+            }
+        }
+        return stackNames;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle args) {
+        final Context context = this.getActivity();
+        final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+        LayoutInflater inflater = getActivity().getLayoutInflater();
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        switch(mCurrentDialogType) {
+            case ADD_STACK_DIALOG:
+                createAddStackDialog(context, inflater, builder, ssp);
+                break;
+            case ADD_STACK_PICK_APP_DIALOG:
+                createAddStackPickAppDialog(context, inflater, builder, ssp);
+                break;
+            case MOVE_TASK_DIALOG:
+                createMoveTaskDialog(context, inflater, builder, ssp);
+                break;
+            case RESIZE_STACK_PICK_STACK_DIALOG:
+                createResizeStackPickStackDialog(context, inflater, builder, ssp);
+                break;
+            case RESIZE_STACK_DIALOG:
+                createResizeStackDialog(context, inflater, builder, ssp);
+                break;
+        }
+        return builder.create();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 237d4f0..72040fe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
+import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
 import android.app.SearchManager;
@@ -49,10 +50,12 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
@@ -64,6 +67,8 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
@@ -228,6 +233,23 @@
         return null;
     }
 
+    /** Returns a list of all the launcher apps sorted by name. */
+    public List<ResolveInfo> getLauncherApps() {
+        if (mPm == null) return new ArrayList<ResolveInfo>();
+
+        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        List<ResolveInfo> activities = mPm.queryIntentActivities(mainIntent, 0 /* flags */);
+        Collections.sort(activities, new Comparator<ResolveInfo>() {
+            @Override
+            public int compare(ResolveInfo o1, ResolveInfo o2) {
+                return getActivityLabel(o1.activityInfo).compareTo(
+                        getActivityLabel(o2.activityInfo));
+            }
+        });
+        return activities;
+    }
+
     /** Returns whether the recents is currently running */
     public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
             AtomicBoolean isHomeTopMost) {
@@ -250,6 +272,64 @@
         return false;
     }
 
+    /** Create a new stack. */
+    public void createNewStack(int displayId, Rect bounds, Intent activity) {
+        try {
+            IActivityContainer container = mIam.createStackOnDisplay(displayId);
+            if (container != null) {
+                // Resize the stack
+                resizeStack(container.getStackId(), bounds);
+                // Start the new activity on that stack
+                container.startActivity(activity);
+            }
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /** Resizes a stack. */
+    public void resizeStack(int stackId, Rect bounds) {
+        if (mIam == null) return;
+
+        try {
+            mIam.resizeStack(stackId, bounds);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /** Returns the stack info for all stacks. */
+    public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
+        if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
+
+        try {
+            SparseArray<ActivityManager.StackInfo> stacks =
+                    new SparseArray<ActivityManager.StackInfo>();
+            List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
+            int stackCount = infos.size();
+            for (int i = 0; i < stackCount; i++) {
+                ActivityManager.StackInfo info = infos.get(i);
+                stacks.put(info.stackId, info);
+            }
+            return stacks;
+        } catch (RemoteException e) {
+            e.printStackTrace();
+            return new SparseArray<ActivityManager.StackInfo>();
+        }
+    }
+
+    /** Returns the focused stack id. */
+    public int getFocusedStack() {
+        if (mIam == null) return -1;
+
+        try {
+            return mIam.getFocusedStackId();
+        } catch (RemoteException e) {
+            e.printStackTrace();
+            return -1;
+        }
+    }
+
     /** Returns whether the specified task is in the home stack */
     public boolean isInHomeStack(int taskId) {
         if (mAm == null) return false;
@@ -313,7 +393,7 @@
         return thumbnail;
     }
 
-    /** Moves a task to the front with the specified activity options */
+    /** Moves a task to the front with the specified activity options. */
     public void moveTaskToFront(int taskId, ActivityOptions opts) {
         if (mAm == null) return;
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
@@ -326,6 +406,18 @@
         }
     }
 
+    /** Moves a task to another stack. */
+    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+        if (mIam == null) return;
+        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
+
+        try {
+            mIam.moveTaskToStack(taskId, stackId, toTop);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
     /** Removes the task */
     public void removeTask(int taskId) {
         if (mAm == null) return;
@@ -524,6 +616,13 @@
     }
 
     /**
+     * Returns a system property.
+     */
+    public String getSystemProperty(String key) {
+        return SystemProperties.get(key);
+    }
+
+    /**
      * Returns the window rect.
      */
     public Rect getWindowRect() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 3d25c80..788e473 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -20,9 +20,12 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.SparseArray;
+import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
@@ -60,7 +63,7 @@
     SystemServicesProxy mSystemServicesProxy;
 
     List<ActivityManager.RecentTaskInfo> mRawTasks;
-    TaskStack mStack;
+    SparseArray<TaskStack> mStacks = new SparseArray<>();
     HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
             new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
 
@@ -90,21 +93,28 @@
     synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
         if (DEBUG) Log.d(TAG, "preloadPlan");
 
+        // This activity info cache will be used for both preloadPlan() and executePlan()
         mActivityInfoCache.clear();
-        mStack = new TaskStack();
+
+        // TODO (multi-display): Currently assume the primary display
+        Rect displayBounds = mSystemServicesProxy.getWindowRect();
 
         Resources res = mContext.getResources();
-        ArrayList<Task> loadedTasks = new ArrayList<Task>();
+        SparseArray<ArrayList<Task>> stacksTasks = new SparseArray<>();
         if (mRawTasks == null) {
             preloadRawTasks(isTopTaskHome);
         }
+        int firstStackId = -1;
         int taskCount = mRawTasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+            if (firstStackId < 0) {
+                firstStackId = t.stackId;
+            }
 
             // Compose the task key
-            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
-                    t.firstActiveTime, t.lastActiveTime);
+            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
+                    t.userId, t.firstActiveTime, t.lastActiveTime);
 
             // Get an existing activity info handle if possible
             Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
@@ -143,14 +153,42 @@
                     iconFilename);
             task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
             if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
-            loadedTasks.add(task);
-        }
-        mStack.setTasks(loadedTasks);
-        mStack.createAffiliatedGroupings(mConfig);
 
-        // Assertion
-        if (mStack.getTaskCount() != mRawTasks.size()) {
-            throw new RuntimeException("Loading failed");
+            if (!mConfig.multiStackEnabled ||
+                    Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
+                ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
+                if (stackTasks == null) {
+                    stackTasks = new ArrayList<Task>();
+                    stacksTasks.put(firstStackId, stackTasks);
+                }
+                stackTasks.add(task);
+            } else {
+                ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
+                if (stackTasks == null) {
+                    stackTasks = new ArrayList<Task>();
+                    stacksTasks.put(t.stackId, stackTasks);
+                }
+                stackTasks.add(task);
+            }
+        }
+
+        // Initialize the stacks
+        SparseArray<ActivityManager.StackInfo> stackInfos = mSystemServicesProxy.getAllStackInfos();
+        mStacks.clear();
+        int stackCount = stacksTasks.size();
+        for (int i = 0; i < stackCount; i++) {
+            int stackId = stacksTasks.keyAt(i);
+            ActivityManager.StackInfo info = stackInfos.get(stackId);
+            ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
+            TaskStack stack = new TaskStack(stackId);
+            if (Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
+                stack.setBounds(displayBounds, displayBounds);
+            } else {
+                stack.setBounds(info.bounds, displayBounds);
+            }
+            stack.setTasks(stackTasks);
+            stack.createAffiliatedGroupings(mConfig);
+            mStacks.put(stackId, stack);
         }
     }
 
@@ -166,72 +204,93 @@
         Resources res = mContext.getResources();
 
         // Iterate through each of the tasks and load them according to the load conditions.
-        ArrayList<Task> tasks = mStack.getTasks();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-            Task task = tasks.get(i);
-            Task.TaskKey taskKey = task.key;
+        int stackCount = mStacks.size();
+        for (int j = 0; j < stackCount; j++) {
+            ArrayList<Task> tasks = mStacks.valueAt(j).getTasks();
+            int taskCount = tasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+                Task task = tasks.get(i);
+                Task.TaskKey taskKey = task.key;
 
-            // Get an existing activity info handle if possible
-            Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
-            ActivityInfoHandle infoHandle;
-            boolean hadCachedActivityInfo = false;
-            if (mActivityInfoCache.containsKey(cnKey)) {
-                infoHandle = mActivityInfoCache.get(cnKey);
-                hadCachedActivityInfo = true;
-            } else {
-                infoHandle = new ActivityInfoHandle();
-            }
-
-            boolean isRunningTask = (task.key.id == opts.runningTaskId);
-            boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
-            boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
-            // If requested, skip the running task
-            if (opts.onlyLoadPausedActivities && isRunningTask) {
-                continue;
-            }
-
-            if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
-                if (task.activityIcon == null) {
-                    if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
-                    task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
-                            mSystemServicesProxy, res, infoHandle, true);
+                // Get an existing activity info handle if possible
+                Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+                ActivityInfoHandle infoHandle;
+                boolean hadCachedActivityInfo = false;
+                if (mActivityInfoCache.containsKey(cnKey)) {
+                    infoHandle = mActivityInfoCache.get(cnKey);
+                    hadCachedActivityInfo = true;
+                } else {
+                    infoHandle = new ActivityInfoHandle();
                 }
-            }
-            if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
-                if (task.thumbnail == null || isRunningTask) {
-                    if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
-                    if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
-                        task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy,
-                                true);
-                    } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
-                        loadQueue.addTask(task);
+
+                boolean isRunningTask = (task.key.id == opts.runningTaskId);
+                boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+                boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+
+                // If requested, skip the running task
+                if (opts.onlyLoadPausedActivities && isRunningTask) {
+                    continue;
+                }
+
+                if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+                    if (task.activityIcon == null) {
+                        if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
+                        task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
+                                t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
                     }
                 }
-            }
+                if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
+                    if (task.thumbnail == null || isRunningTask) {
+                        if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
+                        if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+                            task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+                                    mSystemServicesProxy, true);
+                        } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+                            loadQueue.addTask(task);
+                        }
+                    }
+                }
 
-            // Update the activity info cache
-            if (!hadCachedActivityInfo && infoHandle.info != null) {
-                mActivityInfoCache.put(cnKey, infoHandle);
+                // Update the activity info cache
+                if (!hadCachedActivityInfo && infoHandle.info != null) {
+                    mActivityInfoCache.put(cnKey, infoHandle);
+                }
             }
         }
     }
 
     /**
-     * Composes and returns a TaskStack from the preloaded list of recent tasks.
+     * Returns all TaskStacks from the preloaded list of recent tasks.
      */
-    public TaskStack getTaskStack() {
-        return mStack;
+    public ArrayList<TaskStack> getAllTaskStacks() {
+        ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
+        int stackCount = mStacks.size();
+        for (int i = 0; i < stackCount; i++) {
+            stacks.add(mStacks.valueAt(i));
+        }
+        // Ensure that we have at least one stack
+        if (stacks.isEmpty()) {
+            stacks.add(new TaskStack());
+        }
+        return stacks;
     }
 
     /**
-     * Composes and returns a SpaceNode from the preloaded list of recent tasks.
+     * Returns a specific TaskStack from the preloaded list of recent tasks.
      */
-    public SpaceNode getSpaceNode() {
-        SpaceNode node = new SpaceNode();
-        node.setStack(mStack);
-        return node;
+    public TaskStack getTaskStack(int stackId) {
+        return mStacks.get(stackId);
+    }
+
+    /** Returns whether there are any tasks in any stacks. */
+    public boolean hasTasks() {
+        int stackCount = mStacks.size();
+        for (int i = 0; i < stackCount; i++) {
+            if (mStacks.valueAt(i).getTaskCount() > 0) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
deleted file mode 100644
index 831698a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.model;
-
-import android.graphics.Rect;
-
-import java.util.ArrayList;
-
-
-/**
- * The full recents space is partitioned using a BSP into various nodes that define where task
- * stacks should be placed.
- */
-public class SpaceNode {
-    /* BSP node callbacks */
-    public interface SpaceNodeCallbacks {
-        /** Notifies when a node is added */
-        public void onSpaceNodeAdded(SpaceNode node);
-        /** Notifies when a node is measured */
-        public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
-    }
-
-    SpaceNode mStartNode;
-    SpaceNode mEndNode;
-
-    TaskStack mStack;
-
-    public SpaceNode() {
-        // Do nothing
-    }
-
-    /** Sets the current stack for this space node */
-    public void setStack(TaskStack stack) {
-        mStack = stack;
-    }
-
-    /** Returns the task stack (not null if this is a leaf) */
-    TaskStack getStack() {
-        return mStack;
-    }
-
-    /** Returns whether there are any tasks in any stacks below this node. */
-    public boolean hasTasks() {
-        return (mStack.getTaskCount() > 0) ||
-                (mStartNode != null && mStartNode.hasTasks()) ||
-                (mEndNode != null && mEndNode.hasTasks());
-    }
-
-    /** Returns whether this is a leaf node */
-    boolean isLeafNode() {
-        return (mStartNode == null) && (mEndNode == null);
-    }
-
-    /** Returns all the descendent task stacks */
-    private void getStacksRec(ArrayList<TaskStack> stacks) {
-        if (isLeafNode()) {
-            stacks.add(mStack);
-        } else {
-            mStartNode.getStacksRec(stacks);
-            mEndNode.getStacksRec(stacks);
-        }
-    }
-    public ArrayList<TaskStack> getStacks() {
-        ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
-        getStacksRec(stacks);
-        return stacks;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 55dfe45..0cd55d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -36,6 +36,9 @@
         public void onTaskDataLoaded();
         /* Notifies when a task has been unbound */
         public void onTaskDataUnloaded();
+
+        /* Notifies when a task's stack id has changed. */
+        public void onMultiStackDebugTaskStackIdChanged();
     }
 
     /** The ComponentNameKey represents the unique primary key for a component
@@ -68,14 +71,17 @@
     public static class TaskKey {
         final ComponentNameKey mComponentNameKey;
         public final int id;
+        public int stackId;
         public final Intent baseIntent;
         public final int userId;
         public long firstActiveTime;
         public long lastActiveTime;
 
-        public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
+        public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
+                long lastActiveTime) {
             mComponentNameKey = new ComponentNameKey(intent.getComponent(), userId);
             this.id = id;
+            this.stackId = stackId;
             this.baseIntent = intent;
             this.userId = userId;
             this.firstActiveTime = firstActiveTime;
@@ -92,18 +98,19 @@
             if (!(o instanceof TaskKey)) {
                 return false;
             }
-            return id == ((TaskKey) o).id
-                    && userId == ((TaskKey) o).userId;
+            TaskKey otherKey = (TaskKey) o;
+            return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId;
         }
 
         @Override
         public int hashCode() {
-            return (id << 5) + userId;
+            return Objects.hash(id, stackId, userId);
         }
 
         @Override
         public String toString() {
             return "Task.Key: " + id + ", "
+                    + "s: " + stackId + ", "
                     + "u: " + userId + ", "
                     + "lat: " + lastActiveTime + ", "
                     + baseIntent.getComponent().getPackageName();
@@ -180,6 +187,14 @@
         this.group = group;
     }
 
+    /** Updates the stack id of this task. */
+    public void setStackId(int stackId) {
+        key.stackId = stackId;
+        if (mCb != null) {
+            mCb.onMultiStackDebugTaskStackIdChanged();
+        }
+    }
+
     /** Notifies the callback listeners that this task has been loaded */
     public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
         this.applicationIcon = applicationIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 7f7eee4..5aaea15 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents.model;
 
 import android.graphics.Color;
+import android.graphics.Rect;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.NamedCounter;
@@ -173,33 +174,38 @@
         public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
     }
 
-    /** A pair of indices representing the group and task positions in the stack and group. */
-    public static class GroupTaskIndex {
-        public int groupIndex; // Index in the stack
-        public int taskIndex;  // Index in the group
-
-        public GroupTaskIndex() {}
-
-        public GroupTaskIndex(int gi, int ti) {
-            groupIndex = gi;
-            taskIndex = ti;
-        }
-    }
-
     // The task offset to apply to a task id as a group affiliation
     static final int IndividualTaskIdOffset = 1 << 16;
 
+    public final int id;
+    public final Rect stackBounds = new Rect();
+    public final Rect displayBounds = new Rect();
+
     FilteredTaskList mTaskList = new FilteredTaskList();
     TaskStackCallbacks mCb;
 
     ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
     HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
 
-    /** Sets the callbacks for this task stack */
+    public TaskStack() {
+        this(0);
+    }
+
+    public TaskStack(int stackId) {
+        id = stackId;
+    }
+
+    /** Sets the callbacks for this task stack. */
     public void setCallbacks(TaskStackCallbacks cb) {
         mCb = cb;
     }
 
+    /** Sets the bounds of this stack. */
+    public void setBounds(Rect stackBounds, Rect displayBounds) {
+        this.stackBounds.set(stackBounds);
+        this.displayBounds.set(displayBounds);
+    }
+
     /** Resets this TaskStack. */
     public void reset() {
         mCb = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5152150..ea4c4ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -29,8 +29,10 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewStub;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
+import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -40,6 +42,7 @@
 import com.android.systemui.recents.model.TaskStack;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -56,13 +59,22 @@
         public void onAllTaskViewsDismissed();
         public void onExitToHomeAnimationTriggered();
         public void onScreenPinningRequest();
+
+        public void onMultiStackAddStack();
+        public void onMultiStackResizeStack();
+        public void onMultiStackRemoveStack();
+        public void onMultiStackMoveTask(Task t);
     }
 
     RecentsConfiguration mConfig;
     LayoutInflater mInflater;
     DebugOverlayView mDebugOverlay;
+    ViewStub mMultiStackDebugStub;
+    View mMultiStackDebugView;
+    RecentsViewLayoutAlgorithm mLayoutAlgorithm;
 
     ArrayList<TaskStack> mStacks;
+    List<TaskStackView> mTaskStackViews = new ArrayList<>();
     View mSearchBar;
     RecentsViewCallbacks mCb;
 
@@ -82,6 +94,29 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
         mInflater = LayoutInflater.from(context);
+        mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        if (!mConfig.multiStackEnabled) return;
+
+        mMultiStackDebugStub = (ViewStub) findViewById(R.id.multistack_debug_view_stub);
+        if (mMultiStackDebugView == null) {
+            mMultiStackDebugView = mMultiStackDebugStub.inflate();
+            mMultiStackDebugView.findViewById(R.id.add_stack).setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mCb.onMultiStackAddStack();
+                }
+            });
+            mMultiStackDebugView.findViewById(R.id.resize_stack).setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mCb.onMultiStackResizeStack();
+                }
+            });
+        }
     }
 
     /** Sets the callbacks */
@@ -98,29 +133,18 @@
     public void setTaskStacks(ArrayList<TaskStack> stacks) {
         int numStacks = stacks.size();
 
-        // Make a list of the stack view children only
-        ArrayList<TaskStackView> stackViews = new ArrayList<TaskStackView>();
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                stackViews.add((TaskStackView) child);
-            }
-        }
-
         // Remove all/extra stack views
         int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
         if (mConfig.launchedReuseTaskStackViews) {
-            numTaskStacksToKeep = Math.min(childCount, numStacks);
+            numTaskStacksToKeep = Math.min(mTaskStackViews.size(), numStacks);
         }
-        for (int i = stackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
-            removeView(stackViews.get(i));
-            stackViews.remove(i);
+        for (int i = mTaskStackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
+            removeView(mTaskStackViews.remove(i));
         }
 
         // Update the stack views that we are keeping
         for (int i = 0; i < numTaskStacksToKeep; i++) {
-            TaskStackView tsv = stackViews.get(i);
+            TaskStackView tsv = mTaskStackViews.get(i);
             // If onRecentsHidden is not triggered, we need to the stack view again here
             tsv.reset();
             tsv.setStack(stacks.get(i));
@@ -128,47 +152,53 @@
 
         // Add remaining/recreate stack views
         mStacks = stacks;
-        for (int i = stackViews.size(); i < numStacks; i++) {
+        for (int i = mTaskStackViews.size(); i < numStacks; i++) {
             TaskStack stack = stacks.get(i);
             TaskStackView stackView = new TaskStackView(getContext(), stack);
             stackView.setCallbacks(this);
             addView(stackView);
+            mTaskStackViews.add(stackView);
         }
 
         // Enable debug mode drawing on all the stacks if necessary
         if (mConfig.debugModeEnabled) {
-            for (int i = childCount - 1; i >= 0; i--) {
-                View v = getChildAt(i);
-                if (v != mSearchBar) {
-                    TaskStackView stackView = (TaskStackView) v;
-                    stackView.setDebugOverlay(mDebugOverlay);
-                }
+            for (int i = mTaskStackViews.size() - 1; i >= 0; i--) {
+                TaskStackView stackView = mTaskStackViews.get(i);
+                stackView.setDebugOverlay(mDebugOverlay);
             }
         }
 
+        // Bring the debug view to the front
+        if (mMultiStackDebugView != null) {
+            mMultiStackDebugView.bringToFront();
+        }
+
         // Trigger a new layout
         requestLayout();
     }
 
+    /** Gets the list of task views */
+    List<TaskStackView> getTaskStackViews() {
+        return mTaskStackViews;
+    }
+
     /** Launches the focused task from the first stack if possible */
     public boolean launchFocusedTask() {
         // Get the first stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                TaskStack stack = stackView.mStack;
-                // Iterate the stack views and try and find the focused task
-                List<TaskView> taskViews = stackView.getTaskViews();
-                int taskViewCount = taskViews.size();
-                for (int j = 0; j < taskViewCount; j++) {
-                    TaskView tv = taskViews.get(j);
-                    Task task = tv.getTask();
-                    if (tv.isFocusedTask()) {
-                        onTaskViewClicked(stackView, tv, stack, task, false);
-                        return true;
-                    }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            TaskStack stack = stackView.getStack();
+            // Iterate the stack views and try and find the focused task
+            List<TaskView> taskViews = stackView.getTaskViews();
+            int taskViewCount = taskViews.size();
+            for (int j = 0; j < taskViewCount; j++) {
+                TaskView tv = taskViews.get(j);
+                Task task = tv.getTask();
+                if (tv.isFocusedTask()) {
+                    onTaskViewClicked(stackView, tv, stack, task, false);
+                    return true;
                 }
             }
         }
@@ -178,24 +208,22 @@
     /** Launches the task that Recents was launched from, if possible */
     public boolean launchPreviousTask() {
         // Get the first stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                TaskStack stack = stackView.mStack;
-                ArrayList<Task> tasks = stack.getTasks();
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            TaskStack stack = stackView.getStack();
+            ArrayList<Task> tasks = stack.getTasks();
 
-                // Find the launch task in the stack
-                if (!tasks.isEmpty()) {
-                    int taskCount = tasks.size();
-                    for (int j = 0; j < taskCount; j++) {
-                        if (tasks.get(j).isLaunchTarget) {
-                            Task task = tasks.get(j);
-                            TaskView tv = stackView.getChildViewForTask(task);
-                            onTaskViewClicked(stackView, tv, stack, task, false);
-                            return true;
-                        }
+            // Find the launch task in the stack
+            if (!tasks.isEmpty()) {
+                int taskCount = tasks.size();
+                for (int j = 0; j < taskCount; j++) {
+                    if (tasks.get(j).isLaunchTarget) {
+                        Task task = tasks.get(j);
+                        TaskView tv = stackView.getChildViewForTask(task);
+                        onTaskViewClicked(stackView, tv, stack, task, false);
+                        return true;
                     }
                 }
             }
@@ -209,13 +237,11 @@
         // to ensure that it runs
         ctx.postAnimationTrigger.increment();
 
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.startEnterRecentsAnimation(ctx);
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            stackView.startEnterRecentsAnimation(ctx);
         }
         ctx.postAnimationTrigger.decrement();
     }
@@ -225,13 +251,11 @@
         // We have to increment/decrement the post animation trigger in case there are no children
         // to ensure that it runs
         ctx.postAnimationTrigger.increment();
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.startExitToHomeAnimation(ctx);
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            stackView.startExitToHomeAnimation(ctx);
         }
         ctx.postAnimationTrigger.decrement();
 
@@ -287,22 +311,31 @@
         }
 
         Rect taskStackBounds = new Rect();
-        mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
+        mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
                 mConfig.systemInsets.right, taskStackBounds);
 
-        // Measure each TaskStackView with the full width and height of the window since the 
+        // Measure each TaskStackView with the full width and height of the window since the
         // transition view is a child of that stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar && child.getVisibility() != GONE) {
-                TaskStackView tsv = (TaskStackView) child;
-                // Set the insets to be the top/left inset + search bounds
-                tsv.setStackInsetRect(taskStackBounds);
-                tsv.measure(widthMeasureSpec, heightMeasureSpec);
+        List<TaskStackView> stackViews = getTaskStackViews();
+        List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews,
+                taskStackBounds);
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            if (stackView.getVisibility() != GONE) {
+                // We are going to measure the TaskStackView with the whole RecentsView dimensions,
+                // but the actual stack is going to be inset to the bounds calculated by the layout
+                // algorithm
+                stackView.setStackInsetRect(stackViewsBounds.get(i));
+                stackView.measure(widthMeasureSpec, heightMeasureSpec);
             }
         }
 
+        // Measure the multistack debug view
+        if (mMultiStackDebugView != null) {
+            mMultiStackDebugView.measure(width, height);
+        }
+
         setMeasuredDimension(width, height);
     }
 
@@ -322,14 +355,27 @@
 
         // Layout each TaskStackView with the full width and height of the window since the 
         // transition view is a child of that stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar && child.getVisibility() != GONE) {
-                child.layout(left, top, left + child.getMeasuredWidth(),
-                        top + child.getMeasuredHeight());
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            if (stackView.getVisibility() != GONE) {
+                stackView.layout(left, top, left + stackView.getMeasuredWidth(),
+                        top + stackView.getMeasuredHeight());
             }
         }
+
+        // Layout the multistack debug view
+        if (mMultiStackDebugView != null) {
+            Rect taskStackBounds = new Rect();
+            mConfig.getAvailableTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(),
+                    mConfig.systemInsets.top, mConfig.systemInsets.right, taskStackBounds);
+            mMultiStackDebugView.layout(left,
+                    taskStackBounds.bottom - mConfig.systemInsets.bottom -
+                            mMultiStackDebugView.getMeasuredHeight(),
+                    left + mMultiStackDebugView.getMeasuredWidth(),
+                    taskStackBounds.bottom - mConfig.systemInsets.bottom);
+        }
     }
 
     @Override
@@ -343,41 +389,29 @@
     /** Notifies each task view of the user interaction. */
     public void onUserInteraction() {
         // Get the first stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.onUserInteraction();
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            stackView.onUserInteraction();
         }
     }
 
     /** Focuses the next task in the first stack view */
     public void focusNextTask(boolean forward) {
         // Get the first stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.focusNextTask(forward, true);
-                break;
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        if (!stackViews.isEmpty()) {
+            stackViews.get(0).focusNextTask(forward, true);
         }
     }
 
     /** Dismisses the focused task. */
     public void dismissFocusedTask() {
         // Get the first stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.dismissFocusedTask();
-                break;
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        if (!stackViews.isEmpty()) {
+            stackViews.get(0).dismissFocusedTask();
         }
     }
 
@@ -476,9 +510,16 @@
                     }
                 };
             }
-            opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
-                    b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
-                    sourceView.getHandler(), animStartedListener);
+            if (mConfig.multiStackEnabled) {
+                opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
+                        R.anim.recents_from_unknown_enter,
+                        R.anim.recents_from_unknown_exit,
+                        sourceView.getHandler(), animStartedListener);
+            } else {
+                opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
+                        b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+                        sourceView.getHandler(), animStartedListener);
+            }
         }
 
         final ActivityOptions launchOpts = opts;
@@ -561,13 +602,11 @@
     /** Final callback after Recents is finally hidden. */
     public void onRecentsHidden() {
         // Notify each task stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.onRecentsHidden();
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            stackView.onRecentsHidden();
         }
     }
 
@@ -599,18 +638,23 @@
         }
     }
 
+    @Override
+    public void onMultiStackMoveTask(Task t) {
+        if (mCb != null) {
+            mCb.onMultiStackMoveTask(t);
+        }
+    }
+
     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
 
     @Override
     public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
         // Propagate this event down to each task stack view
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child != mSearchBar) {
-                TaskStackView stackView = (TaskStackView) child;
-                stackView.onPackagesChanged(monitor, packageName, userId);
-            }
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            stackView.onPackagesChanged(monitor, packageName, userId);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
new file mode 100644
index 0000000..eea273c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/* The layout logic for the RecentsView. */
+public class RecentsViewLayoutAlgorithm {
+
+    RecentsConfiguration mConfig;
+
+    public RecentsViewLayoutAlgorithm(RecentsConfiguration config) {
+        mConfig = config;
+    }
+
+    /** Return the relative coordinate given coordinates in another size. */
+    private int getRelativeCoordinate(int availableOffset, int availableSize, int otherCoord, int otherSize) {
+        float relPos = (float) otherCoord / otherSize;
+        return availableOffset + (int) (relPos * availableSize);
+    }
+
+    /**
+     * Computes and returns the bounds that each of the stack views should take up.
+     */
+    List<Rect> computeStackRects(List<TaskStackView> stackViews, Rect availableBounds) {
+        ArrayList<Rect> bounds = new ArrayList<Rect>(stackViews.size());
+        int stackViewsCount = stackViews.size();
+        for (int i = 0; i < stackViewsCount; i++) {
+            TaskStack stack = stackViews.get(i).getStack();
+            Rect sb = stack.stackBounds;
+            Rect db = stack.displayBounds;
+            Rect ab = availableBounds;
+            bounds.add(new Rect(getRelativeCoordinate(ab.left, ab.width(), sb.left, db.width()),
+                    getRelativeCoordinate(ab.top, ab.height(), sb.top, db.height()),
+                    getRelativeCoordinate(ab.left, ab.width(), sb.right, db.width()),
+                    getRelativeCoordinate(ab.top, ab.height(), sb.bottom, db.height())));
+        }
+        return bounds;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 290792a..2318319 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -60,6 +60,8 @@
         public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
         public void onTaskStackFilterTriggered();
         public void onTaskStackUnfilterTriggered();
+
+        public void onMultiStackMoveTask(Task t);
     }
 
     RecentsConfiguration mConfig;
@@ -149,6 +151,11 @@
         requestLayout();
     }
 
+    /** Returns the task stack. */
+    TaskStack getStack() {
+        return mStack;
+    }
+
     /** Sets the debug overlay */
     public void setDebugOverlay(DebugOverlayView overlay) {
         mDebugOverlay = overlay;
@@ -625,6 +632,11 @@
         return mTouchHandler.onGenericMotionEvent(ev);
     }
 
+    /** Returns the region that touch gestures can be started in. */
+    Rect getTouchableRegion() {
+        return mTaskStackBounds;
+    }
+
     @Override
     public void computeScroll() {
         mStackScroller.computeScroll();
@@ -1326,6 +1338,13 @@
         }
     }
 
+    @Override
+    public void onMultiStackMoveTask(TaskView tv) {
+        if (mCb != null) {
+            mCb.onMultiStackMoveTask(tv.getTask());
+        }
+    }
+
     /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index ccad2f1..fabc86d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -96,16 +96,6 @@
         }
         return false;
     }
-    /** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
-    public boolean boundScrollRaw() {
-        float curScroll = getStackScroll();
-        float newScroll = getBoundedStackScroll(curScroll);
-        if (Float.compare(newScroll, curScroll) != 0) {
-            setStackScrollRaw(newScroll);
-            return true;
-        }
-        return false;
-    }
 
     /** Returns the bounded stack scroll */
     float getBoundedStackScroll(float scroll) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 4a6112c..6cdddc5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -123,6 +123,16 @@
             return false;
         }
 
+        int action = ev.getAction();
+        if (mConfig.multiStackEnabled) {
+            // Check if we are within the bounds of the stack view contents
+            if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+                if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
+                    return false;
+                }
+            }
+        }
+
         // Pass through to swipe helper if we are swiping
         mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
         if (mInterceptedBySwipeHelper) {
@@ -131,7 +141,6 @@
 
         boolean wasScrolling = mScroller.isScrolling() ||
                 (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
-        int action = ev.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 // Save the touch down info
@@ -198,6 +207,16 @@
             return false;
         }
 
+        int action = ev.getAction();
+        if (mConfig.multiStackEnabled) {
+            // Check if we are within the bounds of the stack view contents
+            if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+                if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
+                    return false;
+                }
+            }
+        }
+
         // Pass through to swipe helper if we are swiping
         if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
             return true;
@@ -206,7 +225,6 @@
         // Update the velocity tracker
         initVelocityTrackerIfNotExists();
 
-        int action = ev.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 // Save the touch down info
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 82120bf..098f2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -45,6 +45,8 @@
         public void onTaskViewDismissed(TaskView tv);
         public void onTaskViewClipStateChanged(TaskView tv);
         public void onTaskViewFocusChanged(TaskView tv, boolean focused);
+
+        public void onMultiStackMoveTask(TaskView tv);
     }
 
     RecentsConfiguration mConfig;
@@ -457,6 +459,11 @@
             .start();
     }
 
+    /** Enables/disables handling touch on this task view. */
+    void setTouchEnabled(boolean enabled) {
+        setOnClickListener(enabled ? this : null);
+    }
+
     /** Animates this task view if the user does not interact with the stack after a certain time. */
     void startNoUserInteractionAnimation() {
         mHeaderView.startNoUserInteractionAnimation();
@@ -667,6 +674,9 @@
             // Rebind any listeners
             mHeaderView.mApplicationIcon.setOnClickListener(this);
             mHeaderView.mDismissButton.setOnClickListener(this);
+            if (mConfig.multiStackEnabled) {
+                mHeaderView.mMoveTaskButton.setOnClickListener(this);
+            }
             mActionButtonView.setOnClickListener(this);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 if (mConfig.developerOptionsEnabled) {
@@ -687,6 +697,9 @@
             // Unbind any listeners
             mHeaderView.mApplicationIcon.setOnClickListener(null);
             mHeaderView.mDismissButton.setOnClickListener(null);
+            if (mConfig.multiStackEnabled) {
+                mHeaderView.mMoveTaskButton.setOnClickListener(null);
+            }
             mActionButtonView.setOnClickListener(null);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 mHeaderView.mApplicationIcon.setOnLongClickListener(null);
@@ -695,9 +708,9 @@
         mTaskDataLoaded = false;
     }
 
-    /** Enables/disables handling touch on this task view. */
-    void setTouchEnabled(boolean enabled) {
-        setOnClickListener(enabled ? this : null);
+    @Override
+    public void onMultiStackDebugTaskStackIdChanged() {
+        mHeaderView.rebindToTask(mTask);
     }
 
     /**** View.OnClickListener Implementation ****/
@@ -717,6 +730,10 @@
                         }
                     } else if (v == mHeaderView.mDismissButton) {
                         dismissTask();
+                    } else if (v == mHeaderView.mMoveTaskButton) {
+                        if (mCb != null) {
+                            mCb.onMultiStackMoveTask(tv);
+                        }
                     }
                 }
             }, 125);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index e13eed8..b827acc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -56,6 +56,7 @@
     RecentsConfiguration mConfig;
 
     // Header views
+    ImageView mMoveTaskButton;
     ImageView mDismissButton;
     ImageView mApplicationIcon;
     TextView mActivityDescription;
@@ -126,6 +127,10 @@
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+        mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
+        if (mConfig.multiStackEnabled) {
+            mMoveTaskButton.setVisibility(View.VISIBLE);
+        }
 
         // Hide the backgrounds if they are ripple drawables
         if (!Constants.DebugFlags.App.EnableTaskFiltering) {
@@ -188,7 +193,10 @@
             mApplicationIcon.setImageDrawable(t.applicationIcon);
         }
         mApplicationIcon.setContentDescription(t.activityLabel);
-        if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
+        // Always update when multi stack debugging is enabled as the stack id can change
+        if (mConfig.multiStackEnabled) {
+            mActivityDescription.setText("[" + t.key.stackId + "] " + t.activityLabel);
+        } else if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
             mActivityDescription.setText(t.activityLabel);
         }
         // Try and apply the system ui tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index eaec8fa..a0ea25f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -507,7 +507,6 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
-        mSettingsObserver.onChange(false); // set up
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
                 mSettingsObserver);
@@ -557,6 +556,7 @@
 
         createAndAddWindows();
 
+        mSettingsObserver.onChange(false); // set up
         disable(switches[0], false /* animate */);
         setSystemUiVisibility(switches[1], 0xffffffff);
         topAppWindowChanged(switches[2] != 0);
@@ -773,11 +773,7 @@
         }
 
         if (entry.icon != null) {
-            if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
-                entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
-            } else {
-                entry.icon.setColorFilter(null);
-            }
+            entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 6310234..7286907 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -17,6 +17,9 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
 import android.telephony.SubscriptionInfo;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -55,6 +58,7 @@
     private int mAirplaneContentDescription;
     private String mWifiDescription;
     private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
+    private int mIconTint = Color.WHITE;
 
     ViewGroup mWifiGroup;
     ImageView mVpn, mWifi, mAirplane, mNoSims;
@@ -121,6 +125,7 @@
         }
 
         apply();
+        applyIconTint();
     }
 
     @Override
@@ -187,6 +192,9 @@
         for (int i = 0; i < n; i++) {
             inflatePhoneState(subs.get(i).getSubscriptionId());
         }
+        if (isAttachedToWindow()) {
+            applyIconTint();
+        }
     }
 
     private PhoneState getOrInflateState(int subId) {
@@ -315,6 +323,29 @@
         setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
     }
 
+    public void setIconTint(int tint) {
+        boolean changed = tint != mIconTint;
+        mIconTint = tint;
+        if (changed && isAttachedToWindow()) {
+            applyIconTint();
+        }
+    }
+
+    private void applyIconTint() {
+        setTint(mVpn, mIconTint);
+        setTint(mWifi, mIconTint);
+        setTint(mNoSims, mIconTint);
+        setTint(mAirplane, mIconTint);
+        for (int i = 0; i < mPhoneStates.size(); i++) {
+            mPhoneStates.get(i).setIconTint(mIconTint);
+        }
+    }
+
+    private void setTint(ImageView v, int tint) {
+        v.setImageTintMode(PorterDuff.Mode.SRC_ATOP);
+        v.setImageTintList(ColorStateList.valueOf(tint));
+    }
+
     private class PhoneState {
         private final int mSubId;
         private boolean mMobileVisible = false;
@@ -369,6 +400,11 @@
                 event.getText().add(mMobileGroup.getContentDescription());
             }
         }
+
+        public void setIconTint(int tint) {
+            setTint(mMobile, tint);
+            setTint(mMobileType, tint);
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 20dd3e7..d02cd17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -147,6 +147,9 @@
     }
 
     private boolean updateDrawable(boolean withClear) {
+        if (mIcon == null) {
+            return false;
+        }
         Drawable drawable = getIcon(mIcon);
         if (drawable == null) {
             Log.w(TAG, "No icon for slot " + mSlot);
@@ -226,6 +229,12 @@
     }
 
     @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        updateDrawable();
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index fc4d7fe..262d955 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -22,6 +22,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardHostView;
@@ -85,6 +86,7 @@
             mKeyguardView.onResume();
             mKeyguardView.startAppearAnimation();
             mShowingSoon = false;
+            mKeyguardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a2ff64a..7513fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -112,6 +112,7 @@
     private boolean mQsFullyExpanded;
     private boolean mKeyguardShowing;
     private boolean mDozing;
+    private boolean mDozingOnDown;
     private int mStatusBarState;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
@@ -148,7 +149,8 @@
 
     private boolean mBlockTouches;
     private int mNotificationScrimWaitDistance;
-    private boolean mTwoFingerQsExpand;
+    // Used for two finger gesture as well as accessibility shortcut to QS.
+    private boolean mQsExpandImmediate;
     private boolean mTwoFingerQsExpandPossible;
 
     /**
@@ -162,7 +164,7 @@
     private Runnable mLaunchAnimationEndRunnable;
     private boolean mOnlyAffordanceInThisMotion;
     private boolean mKeyguardStatusViewAnimating;
-    private boolean mHeaderAnimatingIn;
+    private boolean mHeaderAnimating;
     private ObjectAnimator mQsContainerAnimator;
     private ValueAnimator mQsSizeChangeAnimator;
 
@@ -221,9 +223,8 @@
         // recompute internal state when qspanel height changes
         mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
             @Override
-            public void onLayoutChange(View v, int left, int top, int right,
-                    int bottom, int oldLeft, int oldTop, int oldRight,
-                    int oldBottom) {
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
                 final int height = bottom - top;
                 final int oldHeight = oldBottom - oldTop;
                 if (height != oldHeight) {
@@ -475,6 +476,13 @@
         }
     }
 
+    public void expandWithQs() {
+        if (mQsExpansionEnabled) {
+            mQsExpandImmediate = true;
+        }
+        expand();
+    }
+
     @Override
     public void fling(float vel, boolean expand) {
         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
@@ -500,7 +508,7 @@
         if (mBlockTouches) {
             return false;
         }
-        resetDownStates(event);
+        initDownStates(event);
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -586,10 +594,11 @@
                 && x < stackScrollerX + mNotificationStackScroller.getWidth();
     }
 
-    private void resetDownStates(MotionEvent event) {
+    private void initDownStates(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             mOnlyAffordanceInThisMotion = false;
             mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
+            mDozingOnDown = isDozing();
         }
     }
 
@@ -634,7 +643,7 @@
         if (mBlockTouches) {
             return false;
         }
-        resetDownStates(event);
+        initDownStates(event);
         if ((!mIsExpanding || mHintAnimationRunning)
                 && !mQsExpanded
                 && mStatusBar.getBarState() != StatusBarState.SHADE) {
@@ -658,7 +667,7 @@
         if (mExpandedHeight != 0) {
             handleQsDown(event);
         }
-        if (!mTwoFingerQsExpand && mQsTracking) {
+        if (!mQsExpandImmediate && mQsTracking) {
             onQsTouch(event);
             if (!mConflictingQsExpansionGesture) {
                 return true;
@@ -675,7 +684,7 @@
         if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
                 && event.getPointerCount() == 2
                 && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
-            mTwoFingerQsExpand = true;
+            mQsExpandImmediate = true;
             requestPanelHeightUpdate();
 
             // Normally, we start listening when the panel is expanded, but here we need to start
@@ -865,26 +874,28 @@
 
     public void setBarState(int statusBarState, boolean keyguardFadingAway,
             boolean goingToFullShade) {
-        boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD
-                || statusBarState == StatusBarState.SHADE_LOCKED;
-        if (!mKeyguardShowing && keyguardShowing) {
-            setQsTranslation(mQsExpansionHeight);
-            mHeader.setTranslationY(0f);
-        }
+        int oldState = mStatusBarState;
+        boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
         setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
         setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
-        if (goingToFullShade) {
+
+        mStatusBarState = statusBarState;
+        mKeyguardShowing = keyguardShowing;
+
+        if (goingToFullShade || (oldState == StatusBarState.KEYGUARD
+                && statusBarState == StatusBarState.SHADE_LOCKED)) {
             animateKeyguardStatusBarOut();
+            animateHeaderSlidingIn();
+        } else if (oldState == StatusBarState.SHADE_LOCKED
+                && statusBarState == StatusBarState.KEYGUARD) {
+            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            animateHeaderSlidingOut();
         } else {
             mKeyguardStatusBar.setAlpha(1f);
             mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
         }
-        mStatusBarState = statusBarState;
-        mKeyguardShowing = keyguardShowing;
+
         updateQsState();
-        if (goingToFullShade) {
-            animateHeaderSlidingIn();
-        }
     }
 
     private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
@@ -906,7 +917,7 @@
             = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
-            mHeaderAnimatingIn = false;
+            mHeaderAnimating = false;
             mQsContainerAnimator = null;
             mQsContainer.removeOnLayoutChangeListener(mQsContainerAnimatorUpdater);
         }
@@ -934,10 +945,13 @@
         @Override
         public boolean onPreDraw() {
             getViewTreeObserver().removeOnPreDrawListener(this);
+            long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
+                    ? 0
+                    : mStatusBar.calculateGoingToFullShadeDelay();
             mHeader.setTranslationY(-mHeader.getCollapsedHeight() - mQsPeekHeight);
             mHeader.animate()
                     .translationY(0f)
-                    .setStartDelay(mStatusBar.calculateGoingToFullShadeDelay())
+                    .setStartDelay(delay)
                     .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
                     .setInterpolator(mFastOutSlowInInterpolator)
                     .start();
@@ -946,7 +960,7 @@
                     mQsContainer.getTranslationY(),
                     mHeader.getCollapsedHeight() + mQsPeekHeight - mQsContainer.getHeight()
                             - mQsContainer.getTop());
-            mQsContainerAnimator.setStartDelay(mStatusBar.calculateGoingToFullShadeDelay());
+            mQsContainerAnimator.setStartDelay(delay);
             mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
             mQsContainerAnimator.setInterpolator(mFastOutSlowInInterpolator);
             mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener);
@@ -955,11 +969,33 @@
             return true;
         }
     };
-    
-    private void animateHeaderSlidingIn() {
-        mHeaderAnimatingIn = true;
-        getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
 
+    private void animateHeaderSlidingIn() {
+        mHeaderAnimating = true;
+        getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+    }
+
+    private void animateHeaderSlidingOut() {
+        mHeaderAnimating = true;
+        mHeader.animate().y(-mHeader.getHeight())
+                .setStartDelay(0)
+                .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mHeader.animate().setListener(null);
+                        mHeaderAnimating = false;
+                        updateQsState();
+                    }
+                })
+                .start();
+        mQsContainer.animate()
+                .y(-mQsContainer.getHeight())
+                .setStartDelay(0)
+                .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .start();
     }
 
     private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
@@ -974,8 +1010,12 @@
     private void animateKeyguardStatusBarOut() {
         mKeyguardStatusBar.animate()
                 .alpha(0f)
-                .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
-                .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2)
+                .setStartDelay(mStatusBar.isKeyguardFadingAway()
+                        ? mStatusBar.getKeyguardFadingAwayDelay()
+                        : 0)
+                .setDuration(mStatusBar.isKeyguardFadingAway()
+                        ? mStatusBar.getKeyguardFadingAwayDuration() / 2
+                        : StackStateAnimator.ANIMATION_DURATION_STANDARD)
                 .setInterpolator(PhoneStatusBar.ALPHA_OUT)
                 .setUpdateListener(mStatusBarAnimateAlphaListener)
                 .withEndAction(mAnimateKeyguardStatusBarInvisibleEndRunnable)
@@ -990,13 +1030,13 @@
         }
     };
 
-    private void animateKeyguardStatusBarIn() {
+    private void animateKeyguardStatusBarIn(long duration) {
         mKeyguardStatusBar.setVisibility(View.VISIBLE);
         mKeyguardStatusBar.setAlpha(0f);
         mKeyguardStatusBar.animate()
                 .alpha(1f)
                 .setStartDelay(0)
-                .setDuration(DOZE_ANIMATION_DURATION)
+                .setDuration(duration)
                 .setInterpolator(mDozeAnimationInterpolator)
                 .setUpdateListener(mStatusBarAnimateAlphaListener)
                 .start();
@@ -1076,9 +1116,12 @@
     }
 
     private void updateQsState() {
-        boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
-        mHeader.setVisibility((mQsExpanded || !mKeyguardShowing) ? View.VISIBLE : View.INVISIBLE);
-        mHeader.setExpanded(mKeyguardShowing || (mQsExpanded && !mStackScrollerOverscrolling));
+        boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
+        mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
+                ? View.VISIBLE
+                : View.INVISIBLE);
+        mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
+                || (mQsExpanded && !mStackScrollerOverscrolling));
         mNotificationStackScroller.setScrollingEnabled(
                 mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
@@ -1116,6 +1159,10 @@
         if (mKeyguardShowing) {
             updateHeaderKeyguard();
         }
+        if (mStatusBarState == StatusBarState.SHADE_LOCKED
+                || mStatusBarState == StatusBarState.KEYGUARD) {
+            updateKeyguardBottomAreaAlpha();
+        }
         if (mStatusBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled) {
             mQsNavbarScrim.setAlpha(getQsExpansionFraction());
@@ -1156,17 +1203,17 @@
     }
 
     private void setQsTranslation(float height) {
-        if (!mHeaderAnimatingIn) {
+        if (!mHeaderAnimating) {
             mQsContainer.setY(height - mQsContainer.getDesiredHeight() + getHeaderTranslation());
         }
-        if (mKeyguardShowing) {
+        if (mKeyguardShowing && !mHeaderAnimating) {
             mHeader.setY(interpolate(getQsExpansionFraction(), -mHeader.getHeight(), 0));
         }
     }
 
     private float calculateQsTopPadding() {
         if (mKeyguardShowing
-                && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
+                && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
 
             // Either QS pushes the notifications down when fully expanded, or QS is fully above the
             // notifications (mostly on tablets). maxNotifications denotes the normal top padding
@@ -1200,7 +1247,7 @@
                 mScrollView.getScrollY(),
                 mAnimateNextTopPaddingChange || animate,
                 mKeyguardShowing
-                        && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted));
+                        && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
         mAnimateNextTopPaddingChange = false;
     }
 
@@ -1313,7 +1360,7 @@
             min = Math.max(min, minHeight);
         }
         int maxHeight;
-        if (mTwoFingerQsExpand || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+        if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
             maxHeight = calculatePanelHeightQsExpanded();
         } else {
             maxHeight = calculatePanelHeightShade();
@@ -1328,10 +1375,10 @@
 
     @Override
     protected void onHeightUpdated(float expandedHeight) {
-        if (!mQsExpanded || mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
             positionClockAndNotifications();
         }
-        if (mTwoFingerQsExpand || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
+        if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
                 && !mQsExpansionFromOverscroll) {
             float t;
             if (mKeyguardShowing) {
@@ -1471,8 +1518,7 @@
      * Hides the header when notifications are colliding with it.
      */
     private void updateHeader() {
-        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
             updateHeaderKeyguard();
         } else {
             updateHeaderShade();
@@ -1481,15 +1527,14 @@
     }
 
     private void updateHeaderShade() {
-        if (!mHeaderAnimatingIn) {
+        if (!mHeaderAnimating) {
             mHeader.setTranslationY(getHeaderTranslation());
         }
         setQsTranslation(mQsExpansionHeight);
     }
 
     private float getHeaderTranslation() {
-        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
             return 0;
         }
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
@@ -1502,30 +1547,42 @@
         return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
     }
 
-    private void updateHeaderKeyguard() {
-        float alphaNotifications;
+    /**
+     * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
+     *         during swiping up
+     */
+    private float getKeyguardContentsAlpha() {
+        float alpha;
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
 
             // When on Keyguard, we hide the header as soon as the top card of the notification
             // stack scroller is close enough (collision distance) to the bottom of the header.
-            alphaNotifications = getNotificationsTopY()
+            alpha = getNotificationsTopY()
                     /
                     (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance);
         } else {
 
             // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
             // soon as we start translating the stack.
-            alphaNotifications = getNotificationsTopY() / mKeyguardStatusBar.getHeight();
+            alpha = getNotificationsTopY() / mKeyguardStatusBar.getHeight();
         }
-        alphaNotifications = MathUtils.constrain(alphaNotifications, 0, 1);
-        alphaNotifications = (float) Math.pow(alphaNotifications, 0.75);
+        alpha = MathUtils.constrain(alpha, 0, 1);
+        alpha = (float) Math.pow(alpha, 0.75);
+        return alpha;
+    }
+
+    private void updateHeaderKeyguard() {
         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
-        mKeyguardStatusBar.setAlpha(Math.min(alphaNotifications, alphaQsExpansion)
+        mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
                 * mKeyguardStatusBarAnimateAlpha);
-        mKeyguardBottomArea.setAlpha(Math.min(1 - getQsExpansionFraction(), alphaNotifications));
         setQsTranslation(mQsExpansionHeight);
     }
 
+    private void updateKeyguardBottomAreaAlpha() {
+        mKeyguardBottomArea.setAlpha(
+                Math.min(getKeyguardContentsAlpha(), 1 - getQsExpansionFraction()));
+    }
+
     private float getNotificationsTopY() {
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
             return getExpandedHeight();
@@ -1555,7 +1612,7 @@
         } else {
             setListening(true);
         }
-        mTwoFingerQsExpand = false;
+        mQsExpandImmediate = false;
         mTwoFingerQsExpandPossible = false;
     }
 
@@ -1573,7 +1630,7 @@
 
     @Override
     protected void setOverExpansion(float overExpansion, boolean isPixels) {
-        if (mConflictingQsExpansionGesture || mTwoFingerQsExpand) {
+        if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
             return;
         }
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
@@ -1593,7 +1650,7 @@
     protected void onTrackingStarted() {
         super.onTrackingStarted();
         if (mQsFullyExpanded) {
-            mTwoFingerQsExpand = true;
+            mQsExpandImmediate = true;
         }
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
                 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
@@ -1756,6 +1813,7 @@
         mSecureCameraLaunchManager.onSwipingStarted();
         requestDisallowInterceptTouchEvent(true);
         mOnlyAffordanceInThisMotion = true;
+        mQsTracking = false;
     }
 
     @Override
@@ -1817,7 +1875,7 @@
     @Override
     protected boolean fullyExpandedClearAllVisible() {
         return mNotificationStackScroller.isDismissViewNotGone()
-                && mNotificationStackScroller.isScrolledToBottom() && !mTwoFingerQsExpand;
+                && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
     }
 
     @Override
@@ -1897,7 +1955,7 @@
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardStatusBar.setVisibility(View.VISIBLE);
             if (animate) {
-                animateKeyguardStatusBarIn();
+                animateKeyguardStatusBarIn(DOZE_ANIMATION_DURATION);
                 mKeyguardBottomArea.startFinishDozeAnimation();
             }
         }
@@ -1947,6 +2005,32 @@
         onEmptySpaceClick(x);
     }
 
+    protected boolean onMiddleClicked() {
+        switch (mStatusBar.getBarState()) {
+            case StatusBarState.KEYGUARD:
+                if (!mDozingOnDown) {
+                    EventLogTags.writeSysuiLockscreenGesture(
+                            EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_UNLOCK_HINT,
+                            0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+                    startUnlockHintAnimation();
+                }
+                return true;
+            case StatusBarState.SHADE_LOCKED:
+                if (!mQsExpanded) {
+                    mStatusBar.goToKeyguard();
+                }
+                return true;
+            case StatusBarState.SHADE:
+
+                // This gets called in the middle of the touch handling, where the state is still
+                // that we are tracking the panel. Collapse the panel after this is done.
+                post(mPostCollapseRunnable);
+                return false;
+            default:
+                return true;
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
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 d86ccee..47034e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -100,7 +100,6 @@
     private boolean mCollapseAfterPeek;
     private boolean mExpanding;
     private boolean mGestureWaitForTouchSlop;
-    private boolean mDozingOnDown;
     private Runnable mPeekRunnable = new Runnable() {
         @Override
         public void run() {
@@ -247,7 +246,6 @@
                 mUpdateFlingOnLayout = false;
                 mPeekTouching = mPanelClosedOnDown;
                 mTouchAboveFalsingThreshold = false;
-                mDozingOnDown = isDozing();
                 if (mVelocityTracker == null) {
                     initVelocityTracker();
                 }
@@ -431,7 +429,6 @@
                 mHasLayoutedSinceDown = false;
                 mUpdateFlingOnLayout = false;
                 mTouchAboveFalsingThreshold = false;
-                mDozingOnDown = isDozing();
                 initVelocityTracker();
                 trackMovement(event);
                 break;
@@ -942,35 +939,14 @@
         }
     }
 
-    private final Runnable mPostCollapseRunnable = new Runnable() {
+    protected final Runnable mPostCollapseRunnable = new Runnable() {
         @Override
         public void run() {
             collapse(false /* delayed */);
         }
     };
-    private boolean onMiddleClicked() {
-        switch (mStatusBar.getBarState()) {
-            case StatusBarState.KEYGUARD:
-                if (!mDozingOnDown) {
-                    EventLogTags.writeSysuiLockscreenGesture(
-                            EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_UNLOCK_HINT,
-                            0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
-                    startUnlockHintAnimation();
-                }
-                return true;
-            case StatusBarState.SHADE_LOCKED:
-                mStatusBar.goToKeyguard();
-                return true;
-            case StatusBarState.SHADE:
 
-                // This gets called in the middle of the touch handling, where the state is still
-                // that we are tracking the panel. Collapse the panel after this is done.
-                post(mPostCollapseRunnable);
-                return false;
-            default:
-                return true;
-        }
-    }
+    protected abstract boolean onMiddleClicked();
 
     protected abstract void onEdgeClicked(boolean right);
 
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 25a2f93..3ee7fb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -32,7 +32,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -50,6 +49,7 @@
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -82,14 +82,12 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.view.Display;
 import android.view.Gravity;
-import android.view.HardwareCanvas;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -97,23 +95,17 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.view.ViewPropertyAnimator;
 import android.view.ViewStub;
-import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.view.animation.PathInterpolator;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.internal.statusbar.StatusBarIcon;
@@ -172,7 +164,6 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
-import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
 import com.android.systemui.volume.VolumeComponent;
 
@@ -212,9 +203,6 @@
 
     private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
 
-    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
-    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
-
     private static final int STATUS_OR_NAV_TRANSIENT =
             View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
     private static final long AUTOHIDE_TIMEOUT_MS = 3000;
@@ -238,6 +226,8 @@
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
+    private int mLightModeIconColor;
+
     PhoneStatusBarPolicy mIconPolicy;
 
     // These are no longer handled by the policy, because we need custom strategies for them
@@ -261,8 +251,7 @@
     AccessibilityController mAccessibilityController;
 
     int mNaturalBarHeight = -1;
-    int mIconSize = -1;
-    int mIconHPadding = -1;
+
     Display mDisplay;
     Point mCurrentDisplaySize = new Point();
 
@@ -278,23 +267,7 @@
     int mPixelFormat;
     Object mQueueLock = new Object();
 
-    // viewgroup containing the normal contents of the statusbar
-    LinearLayout mStatusBarContents;
-
-    // right-hand icons
-    LinearLayout mSystemIconArea;
-    LinearLayout mSystemIcons;
-
-    // left-hand icons
-    LinearLayout mStatusIcons;
-    LinearLayout mStatusIconsKeyguard;
-
-    // the icons themselves
-    IconMerger mNotificationIcons;
-    View mNotificationIconArea;
-
-    // [+>
-    View mMoreIcon;
+    StatusBarIconController mIconController;
 
     // expanded notifications
     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -419,7 +392,6 @@
     private boolean mDozing;
     private boolean mScrimSrcModeEnabled;
 
-    private Interpolator mLinearOutSlowIn;
     private Interpolator mLinearInterpolator = new LinearInterpolator();
     private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
     public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
@@ -562,6 +534,9 @@
         updateDisplaySize();
         mScrimSrcModeEnabled = mContext.getResources().getBoolean(
                 R.bool.config_status_bar_scrim_behind_use_src);
+        mLightModeIconColor = mContext.getResources().getColor(R.color.light_mode_icon_color,
+                mContext.getTheme());
+
         super.start(); // calls createAndAddWindows()
 
         mMediaSessionManager
@@ -610,8 +585,6 @@
         updateDisplaySize(); // populates mDisplayMetrics
         updateResources();
 
-        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
-
         mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                 R.layout.super_status_bar, null);
         mStatusBarWindow.mService = this;
@@ -689,15 +662,6 @@
         // figure out which pixel-format to use for the status bar.
         mPixelFormat = PixelFormat.OPAQUE;
 
-        mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
-        mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
-        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
-        mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
-        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
-        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
-        mNotificationIcons.setOverflowIndicator(mMoreIcon);
-        mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
-
         mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
                 R.id.notification_stack_scroller);
         mStackScroller.setLongPressListener(getNotificationLongClicker());
@@ -741,7 +705,6 @@
         mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
         mHeader.setActivityStarter(this);
         mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
-        mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
         mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
@@ -756,6 +719,9 @@
         // set the inital view visibility
         setAreThereNotifications();
 
+        mIconController = new StatusBarIconController(
+                mContext, mStatusBarView, mKeyguardStatusBar, this);
+
         // Background thread for any controllers that need it.
         mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
         mHandlerThread.start();
@@ -1177,49 +1143,17 @@
         mWindowManager.removeView(mHeadsUpNotificationView);
     }
 
-    public void refreshAllStatusBarIcons() {
-        refreshAllIconsForLayout(mStatusIcons);
-        refreshAllIconsForLayout(mStatusIconsKeyguard);
-        refreshAllIconsForLayout(mNotificationIcons);
-    }
-
-    private void refreshAllIconsForLayout(LinearLayout ll) {
-        final int count = ll.getChildCount();
-        for (int n = 0; n < count; n++) {
-            View child = ll.getChildAt(n);
-            if (child instanceof StatusBarIconView) {
-                ((StatusBarIconView) child).updateDrawable();
-            }
-        }
-    }
-
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
-        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                + " icon=" + icon);
-        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
-        view.set(icon);
-        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
-                LayoutParams.WRAP_CONTENT, mIconSize));
-        view = new StatusBarIconView(mContext, slot, null);
-        view.set(icon);
-        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
-                LayoutParams.WRAP_CONTENT, mIconSize));
+        mIconController.addSystemIcon(slot, index, viewIndex, icon);
     }
 
     public void updateIcon(String slot, int index, int viewIndex,
             StatusBarIcon old, StatusBarIcon icon) {
-        if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                + " old=" + old + " icon=" + icon);
-        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
-        view.set(icon);
-        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
-        view.set(icon);
+        mIconController.updateSystemIcon(slot, index, viewIndex, old, icon);
     }
 
     public void removeIcon(String slot, int index, int viewIndex) {
-        if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
-        mStatusIcons.removeViewAt(viewIndex);
-        mStatusIconsKeyguard.removeViewAt(viewIndex);
+        mIconController.removeSystemIcon(slot, index, viewIndex);
     }
 
     public UserHandle getCurrentUserHandle() {
@@ -1339,7 +1273,6 @@
         if (mNavigationBarView != null) {
             mNavigationBarView.setLayoutDirection(layoutDirection);
         }
-        refreshAllStatusBarIcons();
     }
 
     private void updateShowSearchHoldoff() {
@@ -1480,69 +1413,10 @@
 
     @Override
     protected void updateNotifications() {
-        // TODO: Move this into updateNotificationIcons()?
-        if (mNotificationIcons == null) return;
-
         mNotificationData.filterAndSort();
 
         updateNotificationShade();
-        updateNotificationIcons();
-    }
-
-    private void updateNotificationIcons() {
-        final LinearLayout.LayoutParams params
-            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
-
-        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
-        final int N = activeNotifications.size();
-        ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
-
-        // Filter out notifications with low scores.
-        for (int i = 0; i < N; i++) {
-            Entry ent = activeNotifications.get(i);
-            if (ent.notification.getScore() < HIDE_ICONS_BELOW_SCORE &&
-                    !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
-                continue;
-            }
-            toShow.add(ent.icon);
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "refreshing icons: " + toShow.size() +
-                    " notifications, mNotificationIcons=" + mNotificationIcons);
-        }
-
-        ArrayList<View> toRemove = new ArrayList<View>();
-        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
-            View child = mNotificationIcons.getChildAt(i);
-            if (!toShow.contains(child)) {
-                toRemove.add(child);
-            }
-        }
-
-        final int toRemoveCount = toRemove.size();
-        for (int i = 0; i < toRemoveCount; i++) {
-            mNotificationIcons.removeView(toRemove.get(i));
-        }
-
-        for (int i=0; i<toShow.size(); i++) {
-            View v = toShow.get(i);
-            if (v.getParent() == null) {
-                mNotificationIcons.addView(v, i, params);
-            }
-        }
-
-        // Resort notification icons
-        final int childCount = mNotificationIcons.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View actual = mNotificationIcons.getChildAt(i);
-            StatusBarIconView expected = toShow.get(i);
-            if (actual == expected) {
-                continue;
-            }
-            mNotificationIcons.removeView(expected);
-            mNotificationIcons.addView(expected, i);
-        }
+        mIconController.updateNotificationIcons(mNotificationData);
     }
 
     @Override
@@ -1826,14 +1700,6 @@
         }
     }
 
-    public void showClock(boolean show) {
-        if (mStatusBarView == null) return;
-        View clock = mStatusBarView.findViewById(R.id.clock);
-        if (clock != null) {
-            clock.setVisibility(show ? View.VISIBLE : View.GONE);
-        }
-    }
-
     private int adjustDisableFlags(int state) {
         if (!mLaunchTransitionFadingAway
                 && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
@@ -1882,17 +1748,16 @@
         Log.d(TAG, flagdbg.toString());
 
         if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
-            mSystemIconArea.animate().cancel();
             if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
-                animateStatusBarHide(mSystemIconArea, animate);
+                mIconController.hideSystemIconArea(animate);
             } else {
-                animateStatusBarShow(mSystemIconArea, animate);
+                mIconController.showSystemIconArea(animate);
             }
         }
 
         if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
-            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
-            showClock(show);
+            boolean visible = (state & StatusBarManager.DISABLE_CLOCK) == 0;
+            mIconController.setClockVisibility(visible);
         }
         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
@@ -1916,9 +1781,9 @@
 
         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                animateStatusBarHide(mNotificationIconArea, animate);
+                mIconController.hideNotificationIconArea(animate);
             } else {
-                animateStatusBarShow(mNotificationIconArea, animate);
+                mIconController.showNotificationIconArea(animate);
             }
         }
 
@@ -1929,60 +1794,6 @@
         }
     }
 
-    /**
-     * Animates {@code v}, a view that is part of the status bar, out.
-     */
-    private void animateStatusBarHide(final View v, boolean animate) {
-        v.animate().cancel();
-        if (!animate) {
-            v.setAlpha(0f);
-            v.setVisibility(View.INVISIBLE);
-            return;
-        }
-        v.animate()
-                .alpha(0f)
-                .setDuration(160)
-                .setStartDelay(0)
-                .setInterpolator(ALPHA_OUT)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        v.setVisibility(View.INVISIBLE);
-                    }
-                });
-    }
-
-    /**
-     * Animates {@code v}, a view that is part of the status bar, in.
-     */
-    private void animateStatusBarShow(View v, boolean animate) {
-        v.animate().cancel();
-        v.setVisibility(View.VISIBLE);
-        if (!animate) {
-            v.setAlpha(1f);
-            return;
-        }
-        v.animate()
-                .alpha(1f)
-                .setDuration(320)
-                .setInterpolator(ALPHA_IN)
-                .setStartDelay(50)
-
-                // We need to clean up any pending end action from animateStatusBarHide if we call
-                // both hide and show in the same frame before the animation actually gets started.
-                // cancel() doesn't really remove the end action.
-                .withEndAction(null);
-
-        // Synchronize the motion with the Keyguard fading if necessary.
-        if (mKeyguardFadingAway) {
-            v.animate()
-                    .setDuration(mKeyguardFadingAwayDuration)
-                    .setInterpolator(mLinearOutSlowIn)
-                    .setStartDelay(mKeyguardFadingAwayDelay)
-                    .start();
-        }
-    }
-
     @Override
     protected BaseStatusBar.H createHandler() {
         return new PhoneStatusBar.H();
@@ -2206,8 +2017,7 @@
         // Settings are not available in setup
         if (!mUserSetup) return;
 
-        mNotificationPanel.expand();
-        mNotificationPanel.openQs();
+        mNotificationPanel.expandWithQs();
 
         if (false) postStartTracing();
     }
@@ -2419,6 +2229,16 @@
                 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
             }
 
+            if ((diff & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged) {
+                boolean isTransparentBar = (mStatusBarMode == MODE_TRANSPARENT
+                        || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
+                boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+                boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
+
+                mIconController.setIconTint(
+                        (allowLight && light) ? mLightModeIconColor : Color.WHITE);
+
+            }
             // restore the recents bit
             if (wasRecentsVisible) {
                 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -2668,12 +2488,7 @@
                 mNotificationData.dump(pw, "  ");
             }
 
-            int N = mStatusIcons.getChildCount();
-            pw.println("  system icons: " + N);
-            for (int i=0; i<N; i++) {
-                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
-                pw.println("    [" + i + "] icon=" + ic);
-            }
+            mIconController.dump(pw);
 
             if (false) {
                 pw.println("see the logcat for a dump of the views we have created.");
@@ -2716,6 +2531,12 @@
         if (mSecurityController != null) {
             mSecurityController.dump(fd, pw, args);
         }
+        if (mHeadsUpNotificationView != null) {
+            mHeadsUpNotificationView.dump(fd, pw, args);
+        } else {
+            pw.println("  mHeadsUpNotificationView: null");
+        }
+
         pw.println("SharedPreferences:");
         for (Map.Entry<String, ?> entry : mContext.getSharedPreferences(mContext.getPackageName(),
                 Context.MODE_PRIVATE).getAll().entrySet()) {
@@ -2872,10 +2693,10 @@
         updateDisplaySize(); // populates mDisplayMetrics
 
         updateResources();
-        updateClockSize();
         repositionNavigationBar();
         updateShowSearchHoldoff();
         updateRowStates();
+        mIconController.updateResources();
         mScreenPinningRequest.onConfigurationChanged();
     }
 
@@ -2901,8 +2722,7 @@
         mUserSetupObserver.onChange(false);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
-                mUserSetupObserver,
-                mCurrentUserId);
+                mUserSetupObserver, mCurrentUserId);
     }
 
     private void setHeadsUpVisibility(boolean vis) {
@@ -2932,8 +2752,6 @@
         }
 
         loadDimens();
-        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.linear_out_slow_in);
 
         if (mNotificationPanel != null) {
             mNotificationPanel.updateResources();
@@ -2946,31 +2764,12 @@
         }
     }
 
-    private void updateClockSize() {
-        if (mStatusBarView == null) return;
-        TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
-        if (clock != null) {
-            FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size);
-        }
-    }
     protected void loadDimens() {
         final Resources res = mContext.getResources();
 
         mNaturalBarHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
 
-        int newIconSize = res.getDimensionPixelSize(
-            com.android.internal.R.dimen.status_bar_icon_size);
-        int newIconHPadding = res.getDimensionPixelSize(
-            R.dimen.status_bar_icon_padding);
-
-        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
-//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
-            mIconHPadding = newIconHPadding;
-            mIconSize = newIconSize;
-            //reloadAllNotificationIcons(); // reload the tray
-        }
-
         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
 
         mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
@@ -3179,7 +2978,6 @@
 
     private boolean mDemoModeAllowed;
     private boolean mDemoMode;
-    private DemoStatusIcons mDemoStatusIcons;
 
     @Override
     public void dispatchDemoCommand(String command, Bundle args) {
@@ -3208,10 +3006,8 @@
             dispatchDemoCommandToView(command, args, R.id.battery);
         }
         if (modeChange || command.equals(COMMAND_STATUS)) {
-            if (mDemoStatusIcons == null) {
-                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
-            }
-            mDemoStatusIcons.dispatchDemoCommand(command, args);
+            mIconController.dispatchDemoCommand(command, args);
+
         }
         if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
             mNetworkController.dispatchDemoCommand(command, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
new file mode 100644
index 0000000..5622993
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.BatteryMeterView;
+import com.android.systemui.FontSizeUtils;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.StatusBarIconView;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controls everything regarding the icons in the status bar and on Keyguard, including, but not
+ * limited to: notification icons, signal cluster, additional status icons, and clock in the status
+ * bar.
+ */
+public class StatusBarIconController {
+
+    private Context mContext;
+    private PhoneStatusBar mPhoneStatusBar;
+    private Interpolator mLinearOutSlowIn;
+    private DemoStatusIcons mDemoStatusIcons;
+    private NotificationColorUtil mNotificationColorUtil;
+
+    private LinearLayout mSystemIconArea;
+    private LinearLayout mStatusIcons;
+    private SignalClusterView mSignalCluster;
+    private LinearLayout mStatusIconsKeyguard;
+    private IconMerger mNotificationIcons;
+    private View mNotificationIconArea;
+    private ImageView mMoreIcon;
+    private BatteryMeterView mBatteryMeterView;
+    private TextView mClock;
+
+    private int mIconSize;
+    private int mIconHPadding;
+
+    private int mIconTint = Color.WHITE;
+
+    public StatusBarIconController(Context context, View statusBar, View keyguardStatusBar,
+            PhoneStatusBar phoneStatusBar) {
+        mContext = context;
+        mPhoneStatusBar = phoneStatusBar;
+        mNotificationColorUtil = NotificationColorUtil.getInstance(context);
+        mSystemIconArea = (LinearLayout) statusBar.findViewById(R.id.system_icon_area);
+        mStatusIcons = (LinearLayout) statusBar.findViewById(R.id.statusIcons);
+        mSignalCluster = (SignalClusterView) statusBar.findViewById(R.id.signal_cluster);
+        mNotificationIconArea = statusBar.findViewById(R.id.notification_icon_area_inner);
+        mNotificationIcons = (IconMerger) statusBar.findViewById(R.id.notificationIcons);
+        mMoreIcon = (ImageView) statusBar.findViewById(R.id.moreIcon);
+        mNotificationIcons.setOverflowIndicator(mMoreIcon);
+        mStatusIconsKeyguard = (LinearLayout) keyguardStatusBar.findViewById(R.id.statusIcons);
+        mBatteryMeterView = (BatteryMeterView) statusBar.findViewById(R.id.battery);
+        mClock = (TextView) statusBar.findViewById(R.id.clock);
+        mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.linear_out_slow_in);
+        updateResources();
+    }
+
+    public void updateResources() {
+        mIconSize = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_icon_size);
+        mIconHPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.status_bar_icon_padding);
+        FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size);
+    }
+
+    public void addSystemIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
+        view.set(icon);
+        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize));
+        view = new StatusBarIconView(mContext, slot, null);
+        view.set(icon);
+        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize));
+        applyIconTint();
+    }
+
+    public void updateSystemIcon(String slot, int index, int viewIndex,
+            StatusBarIcon old, StatusBarIcon icon) {
+        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
+        view.set(icon);
+        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
+        view.set(icon);
+        applyIconTint();
+    }
+
+    public void removeSystemIcon(String slot, int index, int viewIndex) {
+        mStatusIcons.removeViewAt(viewIndex);
+        mStatusIconsKeyguard.removeViewAt(viewIndex);
+    }
+
+    public void updateNotificationIcons(NotificationData notificationData) {
+        final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+                mIconSize + 2*mIconHPadding, mPhoneStatusBar.getStatusBarHeight());
+
+        ArrayList<NotificationData.Entry> activeNotifications =
+                notificationData.getActiveNotifications();
+        final int N = activeNotifications.size();
+        ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
+
+        // Filter out ambient notifications.
+        for (int i = 0; i < N; i++) {
+            NotificationData.Entry ent = activeNotifications.get(i);
+            if (notificationData.isAmbient(ent.key)
+                    && !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
+                continue;
+            }
+            toShow.add(ent.icon);
+        }
+
+        ArrayList<View> toRemove = new ArrayList<>();
+        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
+            View child = mNotificationIcons.getChildAt(i);
+            if (!toShow.contains(child)) {
+                toRemove.add(child);
+            }
+        }
+
+        final int toRemoveCount = toRemove.size();
+        for (int i = 0; i < toRemoveCount; i++) {
+            mNotificationIcons.removeView(toRemove.get(i));
+        }
+
+        for (int i=0; i<toShow.size(); i++) {
+            View v = toShow.get(i);
+            if (v.getParent() == null) {
+                mNotificationIcons.addView(v, i, params);
+            }
+        }
+
+        // Resort notification icons
+        final int childCount = mNotificationIcons.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View actual = mNotificationIcons.getChildAt(i);
+            StatusBarIconView expected = toShow.get(i);
+            if (actual == expected) {
+                continue;
+            }
+            mNotificationIcons.removeView(expected);
+            mNotificationIcons.addView(expected, i);
+        }
+
+        applyNotificationIconsTint();
+    }
+
+    public void hideSystemIconArea(boolean animate) {
+        animateHide(mSystemIconArea, animate);
+    }
+
+    public void showSystemIconArea(boolean animate) {
+        animateShow(mSystemIconArea, animate);
+    }
+
+    public void hideNotificationIconArea(boolean animate) {
+        animateHide(mNotificationIconArea, animate);
+    }
+
+    public void showNotificationIconArea(boolean animate) {
+        animateShow(mNotificationIconArea, animate);
+    }
+
+    public void setClockVisibility(boolean visible) {
+        mClock.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    public void dump(PrintWriter pw) {
+        int N = mStatusIcons.getChildCount();
+        pw.println("  system icons: " + N);
+        for (int i=0; i<N; i++) {
+            StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
+            pw.println("    [" + i + "] icon=" + ic);
+        }
+    }
+
+    public void dispatchDemoCommand(String command, Bundle args) {
+        if (mDemoStatusIcons == null) {
+            mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
+        }
+        mDemoStatusIcons.dispatchDemoCommand(command, args);
+    }
+
+    /**
+     * Hides a view.
+     */
+    private void animateHide(final View v, boolean animate) {
+        v.animate().cancel();
+        if (!animate) {
+            v.setAlpha(0f);
+            v.setVisibility(View.INVISIBLE);
+            return;
+        }
+        v.animate()
+                .alpha(0f)
+                .setDuration(160)
+                .setStartDelay(0)
+                .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        v.setVisibility(View.INVISIBLE);
+                    }
+                });
+    }
+
+    /**
+     * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable.
+     */
+    private void animateShow(View v, boolean animate) {
+        v.animate().cancel();
+        v.setVisibility(View.VISIBLE);
+        if (!animate) {
+            v.setAlpha(1f);
+            return;
+        }
+        v.animate()
+                .alpha(1f)
+                .setDuration(320)
+                .setInterpolator(PhoneStatusBar.ALPHA_IN)
+                .setStartDelay(50)
+
+                // We need to clean up any pending end action from animateHide if we call
+                // both hide and show in the same frame before the animation actually gets started.
+                // cancel() doesn't really remove the end action.
+                .withEndAction(null);
+
+        // Synchronize the motion with the Keyguard fading if necessary.
+        if (mPhoneStatusBar.isKeyguardFadingAway()) {
+            v.animate()
+                    .setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration())
+                    .setInterpolator(mLinearOutSlowIn)
+                    .setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay())
+                    .start();
+        }
+    }
+
+    public void setIconTint(int tint) {
+        mIconTint = tint;
+        applyIconTint();
+    }
+
+    private void applyIconTint() {
+        for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
+            StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i);
+            v.setImageTintList(ColorStateList.valueOf(mIconTint));
+        }
+        mSignalCluster.setIconTint(mIconTint);
+        mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
+        mBatteryMeterView.setIconTint(mIconTint);
+        mClock.setTextColor(mIconTint);
+        applyNotificationIconsTint();
+    }
+
+    private void applyNotificationIconsTint() {
+        for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
+            StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
+            boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
+            boolean colorize = !isPreL || isGrayscale(v);
+            if (colorize) {
+                v.setImageTintMode(PorterDuff.Mode.SRC_ATOP);
+                v.setImageTintList(ColorStateList.valueOf(mIconTint));
+            }
+        }
+    }
+
+    private boolean isGrayscale(StatusBarIconView v) {
+        Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
+        if (isGrayscale != null) {
+            return Boolean.TRUE.equals(isGrayscale);
+        }
+        boolean grayscale = mNotificationColorUtil.isGrayscaleIcon(v.getDrawable());
+        v.setTag(R.id.icon_is_grayscale, grayscale);
+        return grayscale;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index ad4c211..18983ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -17,35 +17,25 @@
 package com.android.systemui.statusbar.policy;
 
 import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.ActionListener;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTracker.WifiListener;
 import com.android.systemui.R;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
-
-// TODO: Unify this logic with platform settings (see WifiSettings and AccessPoint). There is a
-// fair amount of complexity here in statuses and logic beyond just connected/disconnected.
-public class AccessPointControllerImpl implements NetworkController.AccessPointController {
+public class AccessPointControllerImpl
+        implements NetworkController.AccessPointController, WifiListener {
     private static final String TAG = "AccessPointController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -63,25 +53,18 @@
 
     private final Context mContext;
     private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
-    private final WifiManager mWifiManager;
+    private final WifiTracker mWifiTracker;
     private final UserManager mUserManager;
-    private final Receiver mReceiver = new Receiver();
 
-    private NetworkControllerImpl mNetworkController;
-    private boolean mScanning;
     private int mCurrentUser;
 
     public AccessPointControllerImpl(Context context) {
         mContext = context;
-        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mWifiTracker = new WifiTracker(context, this, false, true);
         mCurrentUser = ActivityManager.getCurrentUser();
     }
 
-    void setNetworkController(NetworkControllerImpl networkController) {
-        mNetworkController = networkController;
-    }
-
     public boolean canConfigWifi() {
         return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
                 new UserHandle(mCurrentUser));
@@ -96,7 +79,9 @@
         if (callback == null || mCallbacks.contains(callback)) return;
         if (DEBUG) Log.d(TAG, "addCallback " + callback);
         mCallbacks.add(callback);
-        mReceiver.setListening(!mCallbacks.isEmpty());
+        if (mCallbacks.size() == 1) {
+            mWifiTracker.startTracking();
+        }
     }
 
     @Override
@@ -104,37 +89,40 @@
         if (callback == null) return;
         if (DEBUG) Log.d(TAG, "removeCallback " + callback);
         mCallbacks.remove(callback);
-        mReceiver.setListening(!mCallbacks.isEmpty());
+        if (mCallbacks.isEmpty()) {
+            mWifiTracker.stopTracking();
+        }
     }
 
     @Override
     public void scanForAccessPoints() {
-        if (mScanning) return;
         if (DEBUG) Log.d(TAG, "scan!");
-        mScanning = mWifiManager.startScan();
-        // Grab current networks immediately while we wait for scan.
-        updateAccessPoints();
+        mWifiTracker.forceScan();
+    }
+
+    @Override
+    public int getIcon(AccessPoint ap) {
+        int level = ap.getLevel();
+        return ICONS[level >= 0 ? level : 0];
     }
 
     public boolean connect(AccessPoint ap) {
         if (ap == null) return false;
-        if (DEBUG) Log.d(TAG, "connect networkId=" + ap.networkId);
-        if (ap.networkId < 0) {
+        if (DEBUG) Log.d(TAG, "connect networkId=" + ap.getConfig().networkId);
+        if (ap.isSaved()) {
+            mWifiTracker.getManager().connect(ap.getConfig().networkId, mConnectListener);
+        } else {
             // Unknown network, need to add it.
-            if (ap.hasSecurity) {
+            if (ap.getSecurity() != AccessPoint.SECURITY_NONE) {
                 Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
-                intent.putExtra(EXTRA_START_CONNECT_SSID, ap.ssid);
+                intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsid());
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 fireSettingsIntentCallback(intent);
                 return true;
             } else {
-                WifiConfiguration config = new WifiConfiguration();
-                config.SSID = "\"" + ap.ssid + "\"";
-                config.allowedKeyManagement.set(KeyMgmt.NONE);
-                mWifiManager.connect(config, mConnectListener);
+                ap.generateOpenNetworkConfig();
+                mWifiTracker.getManager().connect(ap.getConfig(), mConnectListener);
             }
-        } else {
-            mWifiManager.connect(ap.networkId, mConnectListener);
         }
         return false;
     }
@@ -145,76 +133,28 @@
         }
     }
 
-    private void fireAcccessPointsCallback(AccessPoint[] aps) {
+    private void fireAcccessPointsCallback(List<AccessPoint> aps) {
         for (AccessPointCallback callback : mCallbacks) {
             callback.onAccessPointsChanged(aps);
         }
     }
 
-    private static String trimDoubleQuotes(String v) {
-        return v != null && v.length() >= 2 && v.charAt(0) == '\"'
-                && v.charAt(v.length() - 1) == '\"' ? v.substring(1, v.length() - 1) : v;
+    public void dump(PrintWriter pw) {
+        mWifiTracker.dump(pw);
     }
 
-    private int getConnectedNetworkId(WifiInfo wifiInfo) {
-        return wifiInfo != null ? wifiInfo.getNetworkId() : AccessPoint.NO_NETWORK;
+    @Override
+    public void onWifiStateChanged(int state) {
     }
 
-    private ArrayMap<String, WifiConfiguration> getConfiguredNetworksBySsid() {
-        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
-        if (configs == null || configs.size() == 0) return ArrayMap.EMPTY;
-        final ArrayMap<String, WifiConfiguration> rt = new ArrayMap<String, WifiConfiguration>();
-        for (WifiConfiguration config : configs) {
-            rt.put(trimDoubleQuotes(config.SSID), config);
-        }
-        return rt;
+    @Override
+    public void onConnectedChanged() {
+        fireAcccessPointsCallback(mWifiTracker.getAccessPoints());
     }
 
-    private void updateAccessPoints() {
-        final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        final int connectedNetworkId = getConnectedNetworkId(wifiInfo);
-        if (DEBUG) Log.d(TAG, "connectedNetworkId: " + connectedNetworkId);
-        final List<ScanResult> scanResults = mWifiManager.getScanResults();
-        final ArrayMap<String, WifiConfiguration> configured = getConfiguredNetworksBySsid();
-        if (DEBUG) Log.d(TAG, "scanResults: " + scanResults);
-        final List<AccessPoint> aps = new ArrayList<AccessPoint>(scanResults.size());
-        final ArraySet<String> ssids = new ArraySet<String>();
-        for (ScanResult scanResult : scanResults) {
-            if (scanResult == null) {
-                continue;
-            }
-            final String ssid = scanResult.SSID;
-            if (TextUtils.isEmpty(ssid) || ssids.contains(ssid)) continue;
-            ssids.add(ssid);
-            final WifiConfiguration config = configured.get(ssid);
-            final int level = WifiManager.calculateSignalLevel(scanResult.level, ICONS.length);
-            final AccessPoint ap = new AccessPoint();
-            ap.isConfigured = config != null;
-            ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK;
-            ap.ssid = ssid;
-            // Connected if either:
-            // -The network ID in the active WifiInfo matches this network's ID.
-            // -The network is ephemeral (no configuration) but the SSID matches.
-            ap.isConnected = (ap.networkId != AccessPoint.NO_NETWORK
-                    && ap.networkId == connectedNetworkId) ||
-                    (ap.networkId == WifiConfiguration.INVALID_NETWORK_ID && wifiInfo != null &&
-                    ap.ssid.equals(trimDoubleQuotes(wifiInfo.getSSID())));
-            if (ap.isConnected && mNetworkController != null) {
-                // Ensure we have the connected network's RSSI.
-                ap.level = mNetworkController.getConnectedWifiLevel();
-            } else {
-                ap.level = level;
-            }
-            ap.iconId = ICONS[ap.level];
-            // Based on Settings AccessPoint#getSecurity, keep up to date
-            // with better methods of determining no security or not.
-            ap.hasSecurity = scanResult.capabilities.contains("WEP")
-                    || scanResult.capabilities.contains("PSK")
-                    || scanResult.capabilities.contains("EAP");
-            aps.add(ap);
-        }
-        Collections.sort(aps, mByStrength);
-        fireAcccessPointsCallback(aps.toArray(new AccessPoint[aps.size()]));
+    @Override
+    public void onAccessPointsChanged() {
+        fireAcccessPointsCallback(mWifiTracker.getAccessPoints());
     }
 
     private final ActionListener mConnectListener = new ActionListener() {
@@ -228,49 +168,4 @@
             if (DEBUG) Log.d(TAG, "connect failure reason=" + reason);
         }
     };
-
-    private final Comparator<AccessPoint> mByStrength = new Comparator<AccessPoint> () {
-        @Override
-        public int compare(AccessPoint lhs, AccessPoint rhs) {
-            return -Integer.compare(score(lhs), score(rhs));
-        }
-
-        private int score(AccessPoint ap) {
-            return ap.level + (ap.isConnected ? 20 : 0) + (ap.isConfigured ? 10 : 0);
-        }
-    };
-
-    private final class Receiver extends BroadcastReceiver {
-        private boolean mRegistered;
-
-        public void setListening(boolean listening) {
-            if (listening && !mRegistered) {
-                if (DEBUG) Log.d(TAG, "Registering receiver");
-                final IntentFilter filter = new IntentFilter();
-                filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-                filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-                filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
-                filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
-                filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
-                filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
-                filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-                filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-                mContext.registerReceiver(this, filter);
-                mRegistered = true;
-            } else if (!listening && mRegistered) {
-                if (DEBUG) Log.d(TAG, "Unregistering receiver");
-                mContext.unregisterReceiver(this);
-                mRegistered = false;
-            }
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction());
-            if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
-                updateAccessPoints();
-                mScanning = false;
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 2e3e67a..2e96dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -44,6 +44,8 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
@@ -442,6 +444,27 @@
         mUser = user;
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("HeadsUpNotificationView state:");
+        pw.print("  mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
+        pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
+        pw.print("  mMostRecentPackageName="); pw.println(mMostRecentPackageName);
+        pw.print("  mStartTouchTime="); pw.println(mStartTouchTime);
+        pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
+        pw.print("  mUser="); pw.println(mUser);
+        if (mHeadsUp == null) {
+            pw.println("  mHeadsUp=null");
+        } else {
+            pw.print("  mHeadsUp="); pw.println(mHeadsUp.notification.getKey());
+        }
+        int N = mSnoozedPackages.size();
+        pw.println("  snoozed packages: " + N);
+        for (int i = 0; i < N; i++) {
+            pw.print("    "); pw.print(mSnoozedPackages.valueAt(i));
+            pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
+        }
+    }
+
     private class EdgeSwipeHelper implements Gefingerpoken {
         private static final boolean DEBUG_EDGE_SWIPE = false;
         private final float mTouchSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index ac5602c..ba938cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -28,7 +28,6 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.EriInfo;
 import com.android.systemui.R;
@@ -57,7 +56,6 @@
     // Since some pieces of the phone state are interdependent we store it locally,
     // this could potentially become part of MobileState for simplification/complication
     // of code.
-    private IccCardConstants.State mSimState = IccCardConstants.State.READY;
     private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     private int mDataState = TelephonyManager.DATA_DISCONNECTED;
     private ServiceState mServiceState;
@@ -98,59 +96,10 @@
         updateTelephony();
     }
 
-    /**
-     * Get (the mobile parts of) the carrier string.
-     *
-     * @param currentLabel can be used for concatenation, currently just empty
-     * @param connected whether the device has connection to the internet at all
-     * @param isMobileLabel whether to always return the network or just when data is connected
-     */
-    public String getLabel(String currentLabel, boolean connected, boolean isMobileLabel) {
-        if (!mCurrentState.enabled) {
-            return "";
-        } else {
-            String mobileLabel = "";
-            // We want to show the carrier name if in service and either:
-            // - We are connected to mobile data, or
-            // - We are not connected to mobile data, as long as the *reason* packets are not
-            //   being routed over that link is that we have better connectivity via wifi.
-            // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
-            // is connected, we show nothing.
-            // Otherwise (nothing connected) we show "No internet connection".
-            if (mCurrentState.dataConnected) {
-                mobileLabel = mCurrentState.networkName;
-            } else if (connected || mCurrentState.isEmergency) {
-                if (mCurrentState.connected || mCurrentState.isEmergency) {
-                    // The isEmergencyOnly test covers the case of a phone with no SIM
-                    mobileLabel = mCurrentState.networkName;
-                }
-            } else {
-                mobileLabel = mContext.getString(
-                        R.string.status_bar_settings_signal_meter_disconnected);
-            }
-
-            if (currentLabel.length() != 0) {
-                currentLabel = currentLabel + mNetworkNameSeparator;
-            }
-            // Now for things that should only be shown when actually using mobile data.
-            if (isMobileLabel) {
-                return currentLabel + mobileLabel;
-            } else {
-                return currentLabel
-                        + (mCurrentState.dataConnected ? mobileLabel : currentLabel);
-            }
-        }
-    }
-
     public int getDataContentDescription() {
         return getIcons().mDataContentDescription;
     }
 
-    @VisibleForTesting
-    protected IccCardConstants.State getSimState() {
-        return mSimState;
-    }
-
     public void setAirplaneMode(boolean airplaneMode) {
         mCurrentState.airplaneMode = airplaneMode;
         notifyListenersIfNecessary();
@@ -239,10 +188,13 @@
 
         String contentDescription = getStringIfExists(getContentDescription());
         String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
+
+        boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
+                || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
+
         // Only send data sim callbacks to QS.
         if (mCurrentState.dataSim) {
-            int qsTypeIcon = mCurrentState.dataConnected ?
-                    icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
+            int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
             int length = mSignalsChangedCallbacks.size();
             for (int i = 0; i < length; i++) {
                 mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
@@ -257,8 +209,6 @@
                         icons.mIsWide && qsTypeIcon != 0);
             }
         }
-        boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
-                || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
         int typeIcon = showDataIcon ? icons.mDataType : 0;
         int signalClustersLength = mSignalClusters.size();
         for (int i = 0; i < signalClustersLength; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 3cffc85..9212837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -18,6 +18,10 @@
 
 import android.content.Intent;
 
+import com.android.settingslib.wifi.AccessPoint;
+
+import java.util.List;
+
 public interface NetworkController {
 
     boolean hasMobileDataFeature();
@@ -50,25 +54,14 @@
         void addAccessPointCallback(AccessPointCallback callback);
         void removeAccessPointCallback(AccessPointCallback callback);
         void scanForAccessPoints();
+        int getIcon(AccessPoint ap);
         boolean connect(AccessPoint ap);
         boolean canConfigWifi();
 
         public interface AccessPointCallback {
-            void onAccessPointsChanged(AccessPoint[] accessPoints);
+            void onAccessPointsChanged(List<AccessPoint> accessPoints);
             void onSettingsActivityTriggered(Intent settingsIntent);
         }
-
-        public static class AccessPoint {
-            public static final int NO_NETWORK = -1;  // see WifiManager
-
-            public int networkId;
-            public int iconId;
-            public String ssid;
-            public boolean isConnected;
-            public boolean isConfigured;
-            public boolean hasSecurity;
-            public int level;  // 0 - 5
-        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 1d37ee3..bb3eb7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -17,8 +17,6 @@
 package com.android.systemui.statusbar.policy;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
-import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -84,12 +82,6 @@
     private final AccessPointControllerImpl mAccessPoints;
     private final MobileDataControllerImpl mMobileDataController;
 
-    // Network types that replace the carrier label if the device does not support mobile data.
-    private boolean mBluetoothTethered = false;
-    private boolean mEthernetConnected = false;
-
-    // state of inet connection
-    private boolean mConnected = false;
     private boolean mInetCondition; // Used for Logging and demo.
 
     // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are
@@ -107,8 +99,6 @@
 
     // All the callbacks.
     private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>();
-    private ArrayList<CarrierLabelListener> mCarrierListeners =
-            new ArrayList<CarrierLabelListener>();
     private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
     private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
             new ArrayList<NetworkSignalChangedCallback>();
@@ -166,7 +156,6 @@
 
         // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
         updateAirplaneMode(true /* force callback */);
-        mAccessPoints.setNetworkController(this);
     }
 
     private void registerListeners() {
@@ -222,11 +211,6 @@
         listener.setEmergencyCallsOnly(isEmergencyOnly());
     }
 
-    public void addCarrierLabel(CarrierLabelListener listener) {
-        mCarrierListeners.add(listener);
-        refreshCarrierLabel();
-    }
-
     private void notifyMobileDataEnabled(boolean enabled) {
         final int length = mSignalsChangedCallbacks.size();
         for (int i = 0; i < length; i++) {
@@ -288,9 +272,6 @@
         for (int i = 0; i < length; i++) {
             mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly);
         }
-        // If the emergency has a chance to change, then so does the carrier
-        // label.
-        refreshCarrierLabel();
     }
 
     public void addSignalCluster(SignalCluster cluster) {
@@ -342,7 +323,6 @@
         mCurrentUserId = newUserId;
         mAccessPoints.onUserSwitched(newUserId);
         updateConnectivity();
-        refreshCarrierLabel();
     }
 
     @Override
@@ -354,14 +334,12 @@
         if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) ||
                 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
             updateConnectivity();
-            refreshCarrierLabel();
         } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
             mConfig = Config.readConfig(mContext);
             handleConfigurationChanged();
         } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
             refreshLocale();
             updateAirplaneMode(false);
-            refreshCarrierLabel();
         } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
             // We are using different subs now, we might be able to make calls.
             recalculateEmergency();
@@ -397,7 +375,6 @@
             mobileSignalController.setConfiguration(mConfig);
         }
         refreshLocale();
-        refreshCarrierLabel();
     }
 
     private void updateMobileControllers() {
@@ -503,7 +480,6 @@
                 mobileSignalController.setAirplaneMode(mAirplaneMode);
             }
             notifyListeners();
-            refreshCarrierLabel();
         }
     }
 
@@ -567,10 +543,7 @@
             Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
         }
 
-        mConnected = !mConnectedTransports.isEmpty();
         mInetCondition = !mValidatedTransports.isEmpty();
-        mBluetoothTethered = mConnectedTransports.get(TRANSPORT_BLUETOOTH);
-        mEthernetConnected = mConnectedTransports.get(TRANSPORT_ETHERNET);
 
         pushConnectivityToSignals();
     }
@@ -589,59 +562,6 @@
                 mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0);
     }
 
-    /**
-     * Recalculate and update the carrier label.
-     */
-    void refreshCarrierLabel() {
-        Context context = mContext;
-
-        WifiSignalController.WifiState wifiState = mWifiSignalController.getState();
-        String label = "";
-        for (MobileSignalController controller : mMobileSignalControllers.values()) {
-            label = controller.getLabel(label, mConnected, mHasMobileDataFeature);
-        }
-
-        // TODO Simplify this ugliness, some of the flows below shouldn't be possible anymore
-        // but stay for the sake of history.
-        if (mBluetoothTethered && !mHasMobileDataFeature) {
-            label = mContext.getString(R.string.bluetooth_tethered);
-        }
-
-        if (mEthernetConnected && !mHasMobileDataFeature) {
-            label = context.getString(R.string.ethernet_label);
-        }
-
-        if (mAirplaneMode && !isEmergencyOnly()) {
-            // combined values from connected wifi take precedence over airplane mode
-            if (wifiState.connected && mHasMobileDataFeature) {
-                // Suppress "No internet connection." from mobile if wifi connected.
-                label = "";
-            } else {
-                 if (!mHasMobileDataFeature) {
-                      label = context.getString(
-                              R.string.status_bar_settings_signal_meter_disconnected);
-                 }
-            }
-        } else if (!isMobileDataConnected() && !wifiState.connected && !mBluetoothTethered &&
-                 !mEthernetConnected && !mHasMobileDataFeature) {
-            // Pretty much no connection.
-            label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
-        }
-
-        // for mobile devices, we always show mobile connection info here (SPN/PLMN)
-        // for other devices, we show whatever network is connected
-        // This is determined above by references to mHasMobileDataFeature.
-        int length = mCarrierListeners.size();
-        for (int i = 0; i < length; i++) {
-            mCarrierListeners.get(i).setCarrierLabel(label);
-        }
-    }
-
-    private boolean isMobileDataConnected() {
-        MobileSignalController controller = getDataController();
-        return controller != null ? controller.getState().dataConnected : false;
-    }
-
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NetworkController state:");
 
@@ -649,10 +569,6 @@
         pw.print("  hasVoiceCallingFeature()=");
         pw.println(hasVoiceCallingFeature());
 
-        pw.println("  - Bluetooth ----");
-        pw.print("  mBtReverseTethered=");
-        pw.println(mBluetoothTethered);
-
         pw.println("  - connectivity ------");
         pw.print("  mConnectedTransports=");
         pw.println(mConnectedTransports);
@@ -669,6 +585,8 @@
             mobileSignalController.dump(pw);
         }
         mWifiSignalController.dump(pw);
+
+        mAccessPoints.dump(pw);
     }
 
     private boolean mDemoMode;
@@ -695,7 +613,6 @@
             mWifiSignalController.resetLastState();
             registerListeners();
             notifyAllListeners();
-            refreshCarrierLabel();
         } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
             String airplane = args.getString("airplane");
             if (airplane != null) {
@@ -787,7 +704,6 @@
                 controller.getState().enabled = show;
                 controller.notifyListeners();
             }
-            refreshCarrierLabel();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 14c3d9c..8379b93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -167,7 +167,6 @@
         if (isDirty()) {
             saveLastState();
             notifyListeners();
-            mNetworkController.refreshCarrierLabel();
         }
     }
 
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 5a90324..6a7201c 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -36,4 +36,6 @@
 # UI it doesn't own. This is necessary to allow screenshots to be taken
 LOCAL_CERTIFICATE := platform
 
+include frameworks/base/packages/SettingsLib/common.mk
+
 include $(BUILD_PACKAGE)
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 9b95d5c..260dea0 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
@@ -69,6 +69,8 @@
     protected TelephonyManager mMockTm;
     protected Config mConfig;
 
+    protected int mSubId;
+
     private NetworkCapabilities mNetCapabilities;
 
     @Override
@@ -100,13 +102,13 @@
 
     protected void setupNetworkController() {
         // For now just pretend to be the data sim, so we can test that too.
-        final int subId = SubscriptionManager.getDefaultDataSubId();
+        mSubId = SubscriptionManager.getDefaultDataSubId();
         SubscriptionInfo subscription = mock(SubscriptionInfo.class);
         List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>();
-        when(subscription.getSubscriptionId()).thenReturn(subId);
+        when(subscription.getSubscriptionId()).thenReturn(mSubId);
         subs.add(subscription);
         mNetworkController.setCurrentSubscriptions(subs);
-        mMobileSignalController = mNetworkController.mMobileSignalControllers.get(subId);
+        mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
         mPhoneStateListener = mMobileSignalController.mPhoneStateListener;
         mSignalCluster = mock(SignalCluster.class);
         mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class);
@@ -291,10 +293,6 @@
         assertEquals("Visibility in status bar", visible, (boolean) visibleArg.getValue());
     }
 
-   protected void assertSimStateEquals(IccCardConstants.State expected) {
-       assertEquals("Sim state", expected, mMobileSignalController.getSimState());
-   }
-
    protected void assertNetworkNameEquals(String expected) {
        assertEquals("Network name", expected, mNetworkController.getMobileNetworkName());
    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 65b0971..7fa3f68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -25,6 +25,7 @@
 import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.systemui.R;
 
@@ -254,16 +255,6 @@
         setCdmaRoaming(false);
     }
 
-    public void testOnReceive_updateSimState_noSim() {
-        Intent intent = new Intent();
-        intent.setAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-
-        mNetworkController.onReceive(mContext, intent);
-
-        assertSimStateEquals(IccCardConstants.State.ABSENT);
-    }
-
     public void testOnReceive_stringsUpdatedAction_spn() {
         String expectedMNetworkName = "Test";
         Intent intent = createStringsUpdatedIntent(true /* showSpn */,
@@ -344,6 +335,7 @@
 
         intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
         intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
+        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);
 
         return intent;
     }
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index b0b2886..20a2c9f 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -100,6 +100,7 @@
     private static final String GLOBAL_ACTION_KEY_USERS = "users";
     private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
     private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
+    private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
 
     private final Context mContext;
     private final WindowManagerFuncs mWindowManagerFuncs;
@@ -291,6 +292,8 @@
                 mItems.add(getSettingsAction());
             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                 mItems.add(getLockdownAction());
+            } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
+                mItems.add(getVoiceAssistAction());
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
@@ -436,6 +439,28 @@
         };
     }
 
+    private Action getVoiceAssistAction() {
+        return new SinglePressAction(com.android.internal.R.drawable.ic_voice_search,
+                R.string.global_action_voice_assist) {
+            @Override
+            public void onPress() {
+                Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mContext.startActivity(intent);
+            }
+
+            @Override
+            public boolean showDuringKeyguard() {
+                return true;
+            }
+
+            @Override
+            public boolean showBeforeProvisioning() {
+                return true;
+            }
+        };
+    }
+
     private Action getLockdownAction() {
         return new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock,
                 R.string.global_action_lockdown) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index ed43d8e..6771988 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -894,7 +894,10 @@
     }
 
     void doInvalidatePanelMenu(int featureId) {
-        PanelFeatureState st = getPanelState(featureId, true);
+        PanelFeatureState st = getPanelState(featureId, false);
+        if (st == null) {
+            return;
+        }
         Bundle savedActionViewStates = null;
         if (st.menu != null) {
             savedActionViewStates = new Bundle();
@@ -933,8 +936,8 @@
             // The panel key was pushed, so set the chording key
             mPanelChordingKey = keyCode;
 
-            PanelFeatureState st = getPanelState(featureId, true);
-            if (!st.isOpen) {
+            PanelFeatureState st = getPanelState(featureId, false);
+            if (st != null && !st.isOpen) {
                 return preparePanel(st, event);
             }
         }
@@ -952,12 +955,14 @@
         if (mPanelChordingKey != 0) {
             mPanelChordingKey = 0;
 
-            if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null)) {
+            final PanelFeatureState st = getPanelState(featureId, false);
+
+            if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null) ||
+                    (st == null)) {
                 return;
             }
 
             boolean playSoundEffect = false;
-            final PanelFeatureState st = getPanelState(featureId, true);
             if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
                     mDecorContentParent.canShowOverflowMenu() &&
                     !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {
@@ -1056,7 +1061,7 @@
 
     @Override
     public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
-        return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags);
+        return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags);
     }
 
     private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
@@ -1149,11 +1154,11 @@
                         mInvalidatePanelMenuRunnable.run();
                     }
 
-                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
 
                     // If we don't have a menu or we're waiting for a full content refresh,
                     // forget it. This is a lingering event that no longer matters.
-                    if (st.menu != null && !st.refreshMenuContent &&
+                    if (st != null && st.menu != null && !st.refreshMenuContent &&
                             cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
                         cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
                         mDecorContentParent.showOverflowMenu();
@@ -1161,15 +1166,19 @@
                 }
             } else {
                 mDecorContentParent.hideOverflowMenu();
-                if (cb != null && !isDestroyed()) {
-                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+                final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+                if (st != null && cb != null && !isDestroyed()) {
                     cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
                 }
             }
             return;
         }
 
-        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+
+        if (st == null) {
+            return;
+        }
 
         // Save the future expanded mode state since closePanel will reset it
         boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode;
@@ -2302,8 +2311,8 @@
             // combination such as Control+C.  Temporarily prepare the panel then mark it
             // unprepared again when finished to ensure that the panel will again be prepared
             // the next time it is shown for real.
-            if (mPreparedPanel == null) {
-                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+            if (st != null && mPreparedPanel == null) {
                 preparePanel(st, ev);
                 handled = performPanelShortcut(st, ev.getKeyCode(), ev,
                         Menu.FLAG_PERFORM_NO_CLOSE);
@@ -3156,7 +3165,7 @@
 
             // If the user is chording a menu shortcut, release the chord since
             // this window lost focus
-            if (!hasWindowFocus && mPanelChordingKey != 0) {
+            if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) {
                 closePanel(FEATURE_OPTIONS_PANEL);
             }
 
@@ -3470,6 +3479,10 @@
         if (!mForcedNavigationBarColor) {
             mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
         }
+        if (a.getBoolean(R.styleable.Window_windowHasLightStatusBar, false)) {
+            decor.setSystemUiVisibility(
+                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
 
         if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
                 >= android.os.Build.VERSION_CODES.HONEYCOMB) {
@@ -3906,8 +3919,8 @@
 
     @Override
     public boolean isShortcutKey(int keyCode, KeyEvent event) {
-        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
-        return st.menu != null && st.menu.isShortcutKey(keyCode, event);
+        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+        return st != null && st.menu != null && st.menu.isShortcutKey(keyCode, event);
     }
 
     private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 7124e5b..dca7db1 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -43,6 +43,7 @@
 import android.graphics.Rect;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
+import android.media.AudioService;
 import android.media.IAudioService;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
@@ -153,6 +154,7 @@
     static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
+    static final int SHORT_PRESS_POWER_GO_HOME = 4;
 
     static final int LONG_PRESS_POWER_NOTHING = 0;
     static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
@@ -376,6 +378,8 @@
     int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     boolean mHasSoftInput = false;
     boolean mTranslucentDecorEnabled = true;
+    boolean mUseTvRouting;
+    boolean mUseMasterVolume;
 
     int mPointerLocationMode = 0; // guarded by mLock
 
@@ -971,6 +975,9 @@
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     launchHomeFromHotKey();
                     break;
+                case SHORT_PRESS_POWER_GO_HOME:
+                    launchHomeFromHotKey();
+                    break;
             }
         }
     }
@@ -1262,6 +1269,10 @@
         mTriplePressOnPowerBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_triplePressOnPowerBehavior);
 
+        mUseTvRouting = AudioService.getPlatformType(mContext) == AudioService.PLATFORM_TELEVISION;
+        mUseMasterVolume = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
+
         readConfigurationDependentBehaviors();
 
         mAccessibilityManager = (AccessibilityManager) context.getSystemService(
@@ -1723,7 +1734,6 @@
             case TYPE_STATUS_BAR_PANEL:
             case TYPE_STATUS_BAR_SUB_PANEL:
             case TYPE_SYSTEM_DIALOG:
-            case TYPE_UNIVERSE_BACKGROUND:
             case TYPE_VOLUME_OVERLAY:
             case TYPE_PRIVATE_PRESENTATION:
                 break;
@@ -1822,8 +1832,6 @@
             return 2;
         }
         switch (type) {
-        case TYPE_UNIVERSE_BACKGROUND:
-            return 1;
         case TYPE_PRIVATE_PRESENTATION:
             return 2;
         case TYPE_WALLPAPER:
@@ -1935,11 +1943,6 @@
     }
 
     @Override
-    public int getAboveUniverseLayer() {
-        return windowTypeToLayerLw(TYPE_SYSTEM_ERROR);
-    }
-
-    @Override
     public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) {
         if (mHasNavigationBar) {
             // For a basic navigation bar, when we are in landscape mode we place
@@ -1997,7 +2000,6 @@
             case TYPE_NAVIGATION_BAR:
             case TYPE_WALLPAPER:
             case TYPE_DREAM:
-            case TYPE_UNIVERSE_BACKGROUND:
             case TYPE_KEYGUARD_SCRIM:
                 return false;
             default:
@@ -2991,7 +2993,7 @@
                         } catch (RemoteException e) {
                         }
                         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-                        startDockOrHome();
+                        startDockOrHome(true /*fromHomeKey*/);
                     }
                 }
             });
@@ -3009,7 +3011,7 @@
             } else {
                 // Otherwise, just launch Home
                 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-                startDockOrHome();
+                startDockOrHome(true /*fromHomeKey*/);
             }
         }
     }
@@ -3769,8 +3771,7 @@
                             + mOverscanScreenWidth;
                     pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
                             + mOverscanScreenHeight;
-                } else if (attrs.type == TYPE_BOOT_PROGRESS
-                        || attrs.type == TYPE_UNIVERSE_BACKGROUND) {
+                } else if (attrs.type == TYPE_BOOT_PROGRESS) {
                     // Boot progress screen always covers entire display.
                     pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
                     pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
@@ -4544,6 +4545,10 @@
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
+                if (mUseTvRouting) {
+                    // On TVs volume keys never go to the foreground app
+                    result &= ~ACTION_PASS_TO_USER;
+                }
                 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                     if (down) {
                         if (interactive && !mScreenshotChordVolumeDownKeyTriggered
@@ -4605,11 +4610,15 @@
                     }
 
                     if ((result & ACTION_PASS_TO_USER) == 0) {
-                        // If we aren't passing to the user and no one else
-                        // handled it send it to the session manager to figure
-                        // out.
-                        MediaSessionLegacyHelper.getHelper(mContext)
-                                .sendVolumeKeyEvent(event, true);
+                        if (mUseTvRouting) {
+                            dispatchDirectAudioEvent(event);
+                        } else {
+                            // If we aren't passing to the user and no one else
+                            // handled it send it to the session manager to
+                            // figure out.
+                            MediaSessionLegacyHelper.getHelper(mContext)
+                                    .sendVolumeKeyEvent(event, true);
+                        }
                         break;
                     }
                 }
@@ -4742,6 +4751,7 @@
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
                 }
+                break;
             }
         }
 
@@ -4854,6 +4864,59 @@
         return false;
     }
 
+    private void dispatchDirectAudioEvent(KeyEvent event) {
+        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+            return;
+        }
+        int keyCode = event.getKeyCode();
+        int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND;
+        String pkgName = mContext.getOpPackageName();
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_UP:
+                try {
+                    if (mUseMasterVolume) {
+                        getAudioService().adjustMasterVolume(AudioManager.ADJUST_RAISE, flags,
+                                pkgName);
+                    } else {
+                        getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
+                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
+                }
+                break;
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+                try {
+                    if (mUseMasterVolume) {
+                        getAudioService().adjustMasterVolume(AudioManager.ADJUST_LOWER, flags,
+                                pkgName);
+                    } else {
+                        getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
+                                AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
+                }
+                break;
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
+                try {
+                    if (event.getRepeatCount() == 0) {
+                        if (mUseMasterVolume) {
+                            getAudioService().adjustMasterVolume(AudioManager.ADJUST_TOGGLE_MUTE,
+                                    flags, pkgName);
+                        } else {
+                            getAudioService().adjustSuggestedStreamVolume(
+                                    AudioManager.ADJUST_TOGGLE_MUTE,
+                                    AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName);
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
+                }
+                break;
+        }
+    }
+
     void dispatchMediaKeyWithWakeLock(KeyEvent event) {
         if (DEBUG_INPUT) {
             Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
@@ -5821,19 +5884,31 @@
         return null;
     }
 
-    void startDockOrHome() {
+    void startDockOrHome(boolean fromHomeKey) {
         awakenDreams();
 
         Intent dock = createHomeDockIntent();
         if (dock != null) {
             try {
+                if (fromHomeKey) {
+                    dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
+                }
                 mContext.startActivityAsUser(dock, UserHandle.CURRENT);
                 return;
             } catch (ActivityNotFoundException e) {
             }
         }
 
-        mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
+        Intent intent;
+
+        if (fromHomeKey) {
+            intent = new Intent(mHomeIntent);
+            intent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
+        } else {
+            intent = mHomeIntent;
+        }
+
+        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
     }
 
     /**
@@ -5848,7 +5923,7 @@
             } catch (RemoteException e) {
             }
             sendCloseSystemWindows();
-            startDockOrHome();
+            startDockOrHome(false /*fromHomeKey*/);
         } else {
             // This code brings home to the front or, if it is already
             // at the front, puts the device to sleep.
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 4e89566..e979a1be 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -1882,4 +1882,15 @@
         }
     }
 
+    /**
+     * For USAGE_IO_OUTPUT, destroy() implies setSurface(null).
+     *
+     */
+    @Override
+    public void destroy() {
+        if((mUsage & USAGE_IO_OUTPUT) != 0) {
+            setSurface(null);
+        }
+        super.destroy();
+    }
 }
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index 20b07e7..0f967fc 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -241,8 +241,7 @@
                 addI64(0);
                 addI64(0);
                 addI64(0);
-            }
-            else {
+            } else {
                 addI32((int)obj.getID(null));
             }
         } else {
@@ -619,6 +618,289 @@
         return mPos;
     }
 
+    private static void addToPack(FieldPacker fp, Object obj) {
+        if (obj instanceof Boolean) {
+            fp.addBoolean(((Boolean)obj).booleanValue());
+            return;
+        }
+
+        if (obj instanceof Byte) {
+            fp.addI8(((Byte)obj).byteValue());
+            return;
+        }
+
+        if (obj instanceof Short) {
+            fp.addI16(((Short)obj).shortValue());
+            return;
+        }
+
+        if (obj instanceof Integer) {
+            fp.addI32(((Integer)obj).intValue());
+            return;
+        }
+
+        if (obj instanceof Long) {
+            fp.addI64(((Long)obj).longValue());
+            return;
+        }
+
+        if (obj instanceof Float) {
+            fp.addF32(((Float)obj).floatValue());
+            return;
+        }
+
+        if (obj instanceof Double) {
+            fp.addF64(((Double)obj).doubleValue());
+            return;
+        }
+
+        if (obj instanceof Byte2) {
+            fp.addI8((Byte2)obj);
+            return;
+        }
+
+        if (obj instanceof Byte3) {
+            fp.addI8((Byte3)obj);
+            return;
+        }
+
+        if (obj instanceof Byte4) {
+            fp.addI8((Byte4)obj);
+            return;
+        }
+
+        if (obj instanceof Short2) {
+            fp.addI16((Short2)obj);
+            return;
+        }
+
+        if (obj instanceof Short3) {
+            fp.addI16((Short3)obj);
+            return;
+        }
+
+        if (obj instanceof Short4) {
+            fp.addI16((Short4)obj);
+            return;
+        }
+
+        if (obj instanceof Int2) {
+            fp.addI32((Int2)obj);
+            return;
+        }
+
+        if (obj instanceof Int3) {
+            fp.addI32((Int3)obj);
+            return;
+        }
+
+        if (obj instanceof Int4) {
+            fp.addI32((Int4)obj);
+            return;
+        }
+
+        if (obj instanceof Long2) {
+            fp.addI64((Long2)obj);
+            return;
+        }
+
+        if (obj instanceof Long3) {
+            fp.addI64((Long3)obj);
+            return;
+        }
+
+        if (obj instanceof Long4) {
+            fp.addI64((Long4)obj);
+            return;
+        }
+
+        if (obj instanceof Float2) {
+            fp.addF32((Float2)obj);
+            return;
+        }
+
+        if (obj instanceof Float3) {
+            fp.addF32((Float3)obj);
+            return;
+        }
+
+        if (obj instanceof Float4) {
+            fp.addF32((Float4)obj);
+            return;
+        }
+
+        if (obj instanceof Double2) {
+            fp.addF64((Double2)obj);
+            return;
+        }
+
+        if (obj instanceof Double3) {
+            fp.addF64((Double3)obj);
+            return;
+        }
+
+        if (obj instanceof Double4) {
+            fp.addF64((Double4)obj);
+            return;
+        }
+
+        if (obj instanceof Matrix2f) {
+            fp.addMatrix((Matrix2f)obj);
+            return;
+        }
+
+        if (obj instanceof Matrix3f) {
+            fp.addMatrix((Matrix3f)obj);
+            return;
+        }
+
+        if (obj instanceof Matrix4f) {
+            fp.addMatrix((Matrix4f)obj);
+            return;
+        }
+
+        if (obj instanceof BaseObj) {
+            fp.addObj((BaseObj)obj);
+            return;
+        }
+    }
+
+    private static int getPackedSize(Object obj) {
+        if (obj instanceof Boolean) {
+            return 1;
+        }
+
+        if (obj instanceof Byte) {
+            return 1;
+        }
+
+        if (obj instanceof Short) {
+            return 2;
+        }
+
+        if (obj instanceof Integer) {
+            return 4;
+        }
+
+        if (obj instanceof Long) {
+            return 8;
+        }
+
+        if (obj instanceof Float) {
+            return 4;
+        }
+
+        if (obj instanceof Double) {
+            return 8;
+        }
+
+        if (obj instanceof Byte2) {
+            return 2;
+        }
+
+        if (obj instanceof Byte3) {
+            return 3;
+        }
+
+        if (obj instanceof Byte4) {
+            return 4;
+        }
+
+        if (obj instanceof Short2) {
+            return 4;
+        }
+
+        if (obj instanceof Short3) {
+            return 6;
+        }
+
+        if (obj instanceof Short4) {
+            return 8;
+        }
+
+        if (obj instanceof Int2) {
+            return 8;
+        }
+
+        if (obj instanceof Int3) {
+            return 12;
+        }
+
+        if (obj instanceof Int4) {
+            return 16;
+        }
+
+        if (obj instanceof Long2) {
+            return 16;
+        }
+
+        if (obj instanceof Long3) {
+            return 24;
+        }
+
+        if (obj instanceof Long4) {
+            return 32;
+        }
+
+        if (obj instanceof Float2) {
+            return 8;
+        }
+
+        if (obj instanceof Float3) {
+            return 12;
+        }
+
+        if (obj instanceof Float4) {
+            return 16;
+        }
+
+        if (obj instanceof Double2) {
+            return 16;
+        }
+
+        if (obj instanceof Double3) {
+            return 24;
+        }
+
+        if (obj instanceof Double4) {
+            return 32;
+        }
+
+        if (obj instanceof Matrix2f) {
+            return 16;
+        }
+
+        if (obj instanceof Matrix3f) {
+            return 36;
+        }
+
+        if (obj instanceof Matrix4f) {
+            return 64;
+        }
+
+        if (obj instanceof BaseObj) {
+            if (RenderScript.sPointerSize == 8) {
+                return 32;
+            } else {
+                return 4;
+            }
+        }
+
+        return 0;
+    }
+
+    static FieldPacker createFieldPack(Object[] args) {
+        int len = 0;
+        for (Object arg : args) {
+            len += getPackedSize(arg);
+        }
+        FieldPacker fp = new FieldPacker(len);
+        for (Object arg : args) {
+            addToPack(fp, arg);
+        }
+        return fp;
+    }
+
     private final byte mData[];
     private int mPos;
     private int mLen;
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 271fe05..94aa857 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -313,6 +313,15 @@
           sizes, depClosures, depFieldIDs);
     }
 
+    native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
+        long[] fieldIDs, long[] values, int[] sizes);
+    synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
+        long[] fieldIDs, long[] values, int[] sizes) {
+      validate();
+      return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
+          values, sizes);
+    }
+
     native void rsnClosureSetArg(long con, long closureID, int index,
       long value, int size);
     synchronized void nClosureSetArg(long closureID, int index, long value,
@@ -330,10 +339,10 @@
       rsnClosureSetGlobal(mContext, closureID, fieldID, value, size);
     }
 
-    native long rsnScriptGroup2Create(long con, long[] closures);
-    synchronized long nScriptGroup2Create(long[] closures) {
+    native long rsnScriptGroup2Create(long con, String cachePath, long[] closures);
+    synchronized long nScriptGroup2Create(String cachePath, long[] closures) {
       validate();
-      return rsnScriptGroup2Create(mContext, closures);
+      return rsnScriptGroup2Create(mContext, cachePath, closures);
     }
 
     native void rsnScriptGroup2Execute(long con, long groupID);
@@ -745,6 +754,12 @@
         return rsnScriptKernelIDCreate(mContext, sid, slot, sig);
     }
 
+    native long  rsnScriptInvokeIDCreate(long con, long sid, int slot);
+    synchronized long nScriptInvokeIDCreate(long sid, int slot) {
+        validate();
+        return rsnScriptInvokeIDCreate(mContext, sid, slot);
+    }
+
     native long  rsnScriptFieldIDCreate(long con, long sid, int slot);
     synchronized long nScriptFieldIDCreate(long sid, int slot) {
         validate();
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index eb1687a..d352130 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -66,6 +66,46 @@
     }
 
     /**
+     * @hide Pending API review
+     * InvokeID is an identifier for an invoke function. It is used
+     * as an identifier for ScriptGroup creation.
+     *
+     * This class should not be directly created. Instead use the method in the
+     * reflected or intrinsic code "getInvokeID_funcname()".
+     *
+     */
+    public static final class InvokeID extends BaseObj {
+        Script mScript;
+        int mSlot;
+        InvokeID(long id, RenderScript rs, Script s, int slot) {
+            super(id, rs);
+            mScript = s;
+            mSlot = slot;
+        }
+    }
+
+    private final SparseArray<InvokeID> mIIDs = new SparseArray<InvokeID>();
+    /**
+     * @hide Pending API review
+     * Only to be used by generated reflected classes.
+     */
+    protected InvokeID createInvokeID(int slot) {
+        InvokeID i = mIIDs.get(slot);
+        if (i != null) {
+            return i;
+        }
+
+        long id = mRS.nScriptInvokeIDCreate(getID(mRS), slot);
+        if (id == 0) {
+            throw new RSDriverException("Failed to create KernelID");
+        }
+
+        i = new InvokeID(id, mRS, this, slot);
+        mIIDs.put(slot, i);
+        return i;
+    }
+
+    /**
      * FieldID is an identifier for a Script + exported field pair. It is used
      * as an identifier for ScriptGroup creation.
      *
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index dcad787..113b896 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -8,9 +8,6 @@
 import java.util.Map;
 
 /**
-   @hide Pending Android public API approval.
- */
-/**
 
 ******************************
 You have tried to change the API from what has been previously approved.
@@ -37,6 +34,8 @@
     private Future mReturnFuture;
     private Map<Script.FieldID, Future> mGlobalFuture;
 
+    private FieldPacker mFP;
+
     private static final String TAG = "Closure";
 
     public Closure(long id, RenderScript rs) {
@@ -92,6 +91,44 @@
       setID(id);
     }
 
+    public Closure(RenderScript rs, Script.InvokeID invokeID,
+        Object[] args, Map<Script.FieldID, Object> globals) {
+      super(0, rs);
+      mFP = FieldPacker.createFieldPack(args);
+
+      mBindings = new HashMap<Script.FieldID, Object>();
+      mGlobalFuture = new HashMap<Script.FieldID, Future>();
+
+      int numValues = globals.size();
+
+      long[] fieldIDs = new long[numValues];
+      long[] values = new long[numValues];
+      int[] sizes = new int[numValues];
+      long[] depClosures = new long[numValues];
+      long[] depFieldIDs = new long[numValues];
+
+      int i = 0;
+      for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
+        Object obj = entry.getValue();
+        Script.FieldID fieldID = entry.getKey();
+        fieldIDs[i] = fieldID.getID(rs);
+        if (obj instanceof UnboundValue) {
+          UnboundValue unbound = (UnboundValue)obj;
+          unbound.addReference(this, fieldID);
+        } else {
+          // TODO(yangni): Verify obj not a future.
+          retrieveValueAndDependenceInfo(rs, i, obj, values,
+              sizes, depClosures, depFieldIDs);
+        }
+        i++;
+      }
+
+      long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
+          values, sizes);
+
+      setID(id);
+    }
+
     private static void retrieveValueAndDependenceInfo(RenderScript rs,
         int index, Object obj, long[] values, int[] sizes, long[] depClosures,
         long[] depFieldIDs) {
@@ -102,6 +139,12 @@
         depClosures[index] = f.getClosure().getID(rs);
         Script.FieldID fieldID = f.getFieldID();
         depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
+        if (obj == null) {
+          // Value is originally created by the owner closure
+          values[index] = 0;
+          sizes[index] = 0;
+          return;
+        }
       } else {
         depClosures[index] = 0;
         depFieldIDs[index] = 0;
@@ -124,6 +167,10 @@
       Future f = mGlobalFuture.get(field);
 
       if (f == null) {
+        // If the field is not bound to this closure, this will return a future
+        // without an associated value (reference). So this is not working for
+        // cross-module (cross-script) linking in this case where a field not
+        // explicitly bound.
         f = new Future(this, field, mBindings.get(field));
         mGlobalFuture.put(field, f);
       }
@@ -163,7 +210,6 @@
           size = 8;
         }
       }
-
       public long value;
       public int size;
     }
@@ -240,12 +286,10 @@
     for (int i = 0; i < closureIDs.length; i++) {
       closureIDs[i] = closures.get(i).getID(rs);
     }
-    long id = rs.nScriptGroup2Create(closureIDs);
+    long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs);
     setID(id);
   }
 
-  // TODO: If this was reflected method, we could enforce the number of
-  // arguments.
   public Object[] execute(Object... inputs) {
     if (inputs.length < mInputs.size()) {
       Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
@@ -302,6 +346,13 @@
       return c;
     }
 
+    public Closure addInvoke(Script.InvokeID invoke, Object[] args,
+        Map<Script.FieldID, Object> globalBindings) {
+      Closure c = new Closure(mRS, invoke, args, globalBindings);
+      mClosures.add(c);
+      return c;
+    }
+
     public UnboundValue addInput() {
       UnboundValue unbound = new UnboundValue();
       mInputs.add(unbound);
@@ -309,8 +360,6 @@
     }
 
     public ScriptGroup2 create(Future... outputs) {
-      // TODO: Save all script groups that have been created and return one that was
-      // saved and matches the outputs.
       ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
       return ret;
     }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index dced99a..b25dd41 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -242,6 +242,37 @@
       depFieldIDs, (size_t)depFieldIDs_length);
 }
 
+static jlong
+nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
+                     jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
+                     jintArray sizeArray) {
+  jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
+  jsize jParamLength = _env->GetArrayLength(paramArray);
+
+  jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
+  jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+  RsScriptFieldID* fieldIDs =
+      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
+  for (int i = 0; i< fieldIDs_length; i++) {
+    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+  }
+
+  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+  jsize values_length = _env->GetArrayLength(valueArray);
+  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
+  for (int i = 0; i < values_length; i++) {
+    values[i] = (uintptr_t)jValues[i];
+  }
+
+  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+  return (jlong)(uintptr_t)rsInvokeClosureCreate(
+      (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
+      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
+      (size_t*)sizes, (size_t)sizes_length);
+}
+
 static void
 nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                jint index, jlong value, jint size) {
@@ -258,7 +289,9 @@
 
 static long
 nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
-                    jlongArray closureArray) {
+                    jstring cacheDir, jlongArray closureArray) {
+  AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
+
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
   jsize numClosures = _env->GetArrayLength(closureArray);
   RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
@@ -266,8 +299,9 @@
     closures[i] = (RsClosure)jClosures[i];
   }
 
-  return (jlong)(uintptr_t)rsScriptGroup2Create((RsContext)con, closures,
-                                                numClosures);
+  return (jlong)(uintptr_t)rsScriptGroup2Create(
+      (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(),
+      closures, numClosures);
 }
 
 static void
@@ -1382,6 +1416,14 @@
         sc.zStart     = limit_ptr[4];
         sc.zEnd       = limit_ptr[5];
         sc.strategy   = RS_FOR_EACH_STRATEGY_DONT_CARE;
+        sc.arrayStart = 0;
+        sc.arrayEnd = 0;
+        sc.array2Start = 0;
+        sc.array2End = 0;
+        sc.array3Start = 0;
+        sc.array3End = 0;
+        sc.array4Start = 0;
+        sc.array4End = 0;
 
         sca = &sc;
     }
@@ -1477,6 +1519,16 @@
 }
 
 static jlong
+nScriptInvokeIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot)
+{
+    if (kLogApi) {
+        ALOGD("nScriptInvokeIDCreate, con(%p) script(%p), slot(%i)", (RsContext)con,
+              (void *)sid, slot);
+    }
+    return (jlong)(uintptr_t)rsScriptInvokeIDCreate((RsContext)con, (RsScript)sid, slot);
+}
+
+static jlong
 nScriptFieldIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot)
 {
     if (kLogApi) {
@@ -1924,6 +1976,7 @@
 {"rsnContextResume",                 "(J)V",                                  (void*)nContextResume },
 {"rsnContextSendMessage",            "(JI[I)V",                               (void*)nContextSendMessage },
 {"rsnClosureCreate",                 "(JJJ[J[J[I[J[J)J",                      (void*)nClosureCreate },
+{"rsnInvokeClosureCreate",           "(JJ[B[J[J[I)J",                         (void*)nInvokeClosureCreate },
 {"rsnClosureSetArg",                 "(JJIJI)V",                              (void*)nClosureSetArg },
 {"rsnClosureSetGlobal",              "(JJJJI)V",                              (void*)nClosureSetGlobal },
 {"rsnAssignName",                    "(JJ[B)V",                               (void*)nAssignName },
@@ -1998,9 +2051,10 @@
 {"rsnScriptCCreate",                 "(JLjava/lang/String;Ljava/lang/String;[BI)J",  (void*)nScriptCCreate },
 {"rsnScriptIntrinsicCreate",         "(JIJ)J",                                (void*)nScriptIntrinsicCreate },
 {"rsnScriptKernelIDCreate",          "(JJII)J",                               (void*)nScriptKernelIDCreate },
+{"rsnScriptInvokeIDCreate",          "(JJI)J",                                (void*)nScriptInvokeIDCreate },
 {"rsnScriptFieldIDCreate",           "(JJI)J",                                (void*)nScriptFieldIDCreate },
 {"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
-{"rsnScriptGroup2Create",            "(J[J)J",                                (void*)nScriptGroup2Create },
+{"rsnScriptGroup2Create",            "(JLjava/lang/String;[J)J",               (void*)nScriptGroup2Create },
 {"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
 {"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
 {"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8c314cf..a712d78 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -170,6 +170,8 @@
 
     private final Rect mTempRect = new Rect();
 
+    private final Rect mTempRect1 = new Rect();
+
     private final Point mTempPoint = new Point();
 
     private final PackageManager mPackageManager;
@@ -2535,57 +2537,6 @@
         }
 
         @Override
-        public  boolean computeClickPointInScreen(int accessibilityWindowId,
-                long accessibilityNodeId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
-                throws RemoteException {
-            final int resolvedWindowId;
-            IAccessibilityInteractionConnection connection = null;
-            Region partialInteractiveRegion = mTempRegion;
-            synchronized (mLock) {
-                // We treat calls from a profile as if made by its parent as profiles
-                // share the accessibility state of the parent. The call below
-                // performs the current profile parent resolution.
-                final int resolvedUserId = mSecurityPolicy
-                        .resolveCallingUserIdEnforcingPermissionsLocked(
-                                UserHandle.USER_CURRENT);
-                if (resolvedUserId != mCurrentUserId) {
-                    return false;
-                }
-                resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
-                final boolean permissionGranted =
-                        mSecurityPolicy.canRetrieveWindowContentLocked(this);
-                if (!permissionGranted) {
-                    return false;
-                } else {
-                    connection = getConnectionLocked(resolvedWindowId);
-                    if (connection == null) {
-                        return false;
-                    }
-                }
-                if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
-                        resolvedWindowId, partialInteractiveRegion)) {
-                    partialInteractiveRegion = null;
-                }
-            }
-            final int interrogatingPid = Binder.getCallingPid();
-            final long identityToken = Binder.clearCallingIdentity();
-            MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
-            try {
-                connection.computeClickPointInScreen(accessibilityNodeId, partialInteractiveRegion,
-                        interactionId, callback, interrogatingPid, interrogatingTid, spec);
-                return true;
-            } catch (RemoteException re) {
-                if (DEBUG) {
-                    Slog.e(LOG_TAG, "Error computeClickPointInScreen().");
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-            return false;
-        }
-
-        @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
             synchronized (mLock) {
@@ -3238,38 +3189,36 @@
             }
 
             synchronized (mLock) {
-                Point point = mClient.computeClickPointInScreen(mConnectionId,
-                        focus.getWindowId(), focus.getSourceNodeId());
+                Rect boundsInScreen = mTempRect;
+                focus.getBoundsInScreen(boundsInScreen);
 
-                if (point == null) {
+                // Clip to the window bounds.
+                Rect windowBounds = mTempRect1;
+                getWindowBounds(focus.getWindowId(), windowBounds);
+                boundsInScreen.intersect(windowBounds);
+                if (boundsInScreen.isEmpty()) {
                     return false;
                 }
 
+                // Apply magnification if needed.
                 MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
                 if (spec != null && !spec.isNop()) {
-                    point.offset((int) -spec.offsetX, (int) -spec.offsetY);
-                    point.x = (int) (point.x * (1 / spec.scale));
-                    point.y = (int) (point.y * (1 / spec.scale));
+                    boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
+                    boundsInScreen.scale(1 / spec.scale);
                 }
 
-                // Make sure the point is within the window.
-                Rect windowBounds = mTempRect;
-                getWindowBounds(focus.getWindowId(), windowBounds);
-                if (!windowBounds.contains(point.x, point.y)) {
-                    return false;
-                }
-
-                // Make sure the point is within the screen.
+                // Clip to the screen bounds.
                 Point screenSize = mTempPoint;
                 mDefaultDisplay.getRealSize(screenSize);
-                if (point.x < 0 || point.x > screenSize.x
-                        || point.y < 0 || point.y > screenSize.y) {
+                boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y);
+                if (boundsInScreen.isEmpty()) {
                     return false;
                 }
 
-                outPoint.set(point.x, point.y);
-                return true;
+                outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY());
             }
+
+            return true;
         }
 
         private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index b9ed89b..f18b5ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -77,6 +77,10 @@
     private static final int STATE_DELEGATING = 0x00000004;
     private static final int STATE_GESTURE_DETECTING = 0x00000005;
 
+    private static final int CLICK_LOCATION_NONE = 0;
+    private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
+    private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
+
     // The maximum of the cosine between the vectors of two moving
     // pointers so they can be considered moving in the same direction.
     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
@@ -942,12 +946,16 @@
      *
      * @param prototype The prototype from which to create the injected events.
      * @param policyFlags The policy flags associated with the event.
+     * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
      */
-    private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
+    private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
+            boolean targetAccessibilityFocus) {
         // Tap with the pointer that last explored.
         final int pointerId = prototype.getPointerId(prototype.getActionIndex());
         final int pointerIdBits = (1 << pointerId);
+        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
         sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
+        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
         sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
     }
 
@@ -1155,7 +1163,8 @@
             final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
 
             Point clickLocation = mTempPoint;
-            if (!computeClickLocation(clickLocation)) {
+            final int result = computeClickLocation(clickLocation);
+            if (result == CLICK_LOCATION_NONE) {
                 return;
             }
 
@@ -1171,7 +1180,8 @@
                     secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
                     coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
                     secondTapUp.getSource(), secondTapUp.getFlags());
-            sendActionDownAndUp(event, policyFlags);
+            final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
+            sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus);
             event.recycle();
         }
 
@@ -1216,7 +1226,7 @@
                 MAX_DRAGGING_ANGLE_COS);
     }
 
-    private boolean computeClickLocation(Point outLocation) {
+    private int computeClickLocation(Point outLocation) {
         MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
         if (lastExploreEvent != null) {
             final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
@@ -1224,14 +1234,17 @@
             outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
             if (!mAms.accessibilityFocusOnlyInActiveWindow()
                     || mLastTouchedWindowId == mAms.getActiveWindowId()) {
-                mAms.getAccessibilityFocusClickPointInScreen(outLocation);
+                if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+                    return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+                } else {
+                    return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
+                }
             }
-            return true;
         }
         if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
-            return true;
+            return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
         }
-        return false;
+        return CLICK_LOCATION_NONE;
     }
 
     /**
@@ -1310,14 +1323,13 @@
                 return;
             }
 
-            int clickLocationX;
-            int clickLocationY;
-
             final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
             final int pointerIndex = mEvent.findPointerIndex(pointerId);
 
             Point clickLocation = mTempPoint;
-            if (!computeClickLocation(clickLocation)) {
+            final int result = computeClickLocation(clickLocation);
+
+            if (result == CLICK_LOCATION_NONE) {
                 return;
             }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 289152b..c1e4994 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -382,6 +382,7 @@
                     // we're now good to go, so start the backup alarms
                     if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
                     startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
+                    scheduleNextFullBackupJob();
                 }
             }
         }
@@ -3853,6 +3854,16 @@
             PackageInfo currentPackage;
 
             try {
+                if (!mEnabled || !mProvisioned) {
+                    // Backups are globally disabled, so don't proceed.
+                    if (DEBUG) {
+                        Slog.i(TAG, "full backup requested but e=" + mEnabled
+                                + " p=" + mProvisioned + "; ignoring");
+                    }
+                    mUpdateSchedule = false;
+                    return;
+                }
+
                 IBackupTransport transport = getTransport(mCurrentTransport);
                 if (transport == null) {
                     Slog.w(TAG, "Transport not present; full data backup not performed");
@@ -4150,6 +4161,17 @@
         long now = System.currentTimeMillis();
         FullBackupEntry entry = null;
 
+        if (!mEnabled || !mProvisioned) {
+            // Backups are globally disabled, so don't proceed.  We also don't reschedule
+            // the job driving automatic backups; that job will be scheduled again when
+            // the user enables backup.
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+                        + " p=" + mProvisioned + "; ignoring");
+            }
+            return false;
+        }
+
         if (DEBUG_SCHEDULING) {
             Slog.i(TAG, "Beginning scheduled full backup operation");
         }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 45fa373..7d156df 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1230,7 +1230,8 @@
         if (mAlarmBatches.size() > 0) {
             final Batch firstWakeup = findFirstWakeupBatchLocked();
             final Batch firstBatch = mAlarmBatches.get(0);
-            if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
+            // always update the kernel alarms, as a backstop against missed wakeups
+            if (firstWakeup != null) {
                 mNextWakeup = firstWakeup.start;
                 setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
             }
@@ -1243,7 +1244,8 @@
                 nextNonWakeup = mNextNonWakeupDeliveryTime;
             }
         }
-        if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) {
+        // always update the kernel alarm, as a backstop against missed wakeups
+        if (nextNonWakeup != 0) {
             mNextNonWakeup = nextNonWakeup;
             setLocked(ELAPSED_REALTIME, nextNonWakeup);
         }
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index bffbb4c2..c9a83ec 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -219,6 +219,7 @@
 
         mWakeLock.acquire();
 
+        Log.i(TAG, "MSG_NEW_DEVICE_STATE ");
         Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
                 mHeadsetState, newName);
         mHandler.sendMessage(msg);
@@ -286,14 +287,16 @@
                 return;
             }
 
-            if (LOG)
-                Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
+            if (LOG) {
+                Slog.v(TAG, "headsetName: " + headsetName +
+                        (state == 1 ? " connected" : " disconnected"));
+            }
 
             if (outDevice != 0) {
-              mAudioManager.setWiredDeviceConnectionState(outDevice, state, headsetName);
+              mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName);
             }
             if (inDevice != 0) {
-              mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName);
+              mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 9f8d665..059dde1 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -72,7 +72,7 @@
     static final boolean DEBUG_DELAYED_SERVICE = ActivityManagerService.DEBUG_SERVICE;
     static final boolean DEBUG_DELAYED_STARTS = DEBUG_DELAYED_SERVICE;
     static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
-    static final boolean LOG_SERVICE_START_STOP = true;
+    static final boolean LOG_SERVICE_START_STOP = false;
     static final String TAG = ActivityManagerService.TAG;
     static final String TAG_MU = ActivityManagerService.TAG_MU;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index df4b6d6..7a493c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -87,6 +87,7 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.AppTransition;
 import com.android.server.wm.WindowManagerService;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -169,6 +170,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IPermissionController;
+import android.os.IProcessInfoService;
 import android.os.IRemoteCallback;
 import android.os.IUserManager;
 import android.os.Looper;
@@ -1854,9 +1856,14 @@
                     synchronized (ActivityManagerService.this) {
                         if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
                                 + (SystemClock.uptimeMillis()-start) + "ms");
-                        mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
-                                memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
-                                memInfo.getKernelUsedSizeKb(), nativeTotalPss);
+                        final long cachedKb = memInfo.getCachedSizeKb();
+                        final long freeKb = memInfo.getFreeSizeKb();
+                        final long zramKb = memInfo.getZramTotalSizeKb();
+                        final long kernelKb = memInfo.getKernelUsedSizeKb();
+                        EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+                                kernelKb*1024, nativeTotalPss*1024);
+                        mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+                                nativeTotalPss);
                     }
                 }
 
@@ -1914,6 +1921,7 @@
                 ServiceManager.addService("cpuinfo", new CpuBinder(this));
             }
             ServiceManager.addService("permission", new PermissionController(this));
+            ServiceManager.addService("processinfo", new ProcessInfoService(this));
 
             ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                     "android", STOCK_PM_FLAGS);
@@ -5617,7 +5625,10 @@
 
     @Override
     public void showBootMessage(final CharSequence msg, final boolean always) {
-        enforceNotIsolatedCaller("showBootMessage");
+        if (Binder.getCallingUid() != Process.myUid()) {
+            // These days only the core system can call this, so apps can't get in
+            // the way of what we show about running them.
+        }
         mWindowManager.showBootMessage(msg, always);
     }
 
@@ -6278,7 +6289,46 @@
             }
         }
     }
-    
+
+    // =========================================================
+    // PROCESS INFO
+    // =========================================================
+
+    static class ProcessInfoService extends IProcessInfoService.Stub {
+        final ActivityManagerService mActivityManagerService;
+        ProcessInfoService(ActivityManagerService activityManagerService) {
+            mActivityManagerService = activityManagerService;
+        }
+
+        @Override
+        public void getProcessStatesFromPids(/*in*/ int[] pids, /*out*/ int[] states) {
+            mActivityManagerService.getProcessStatesForPIDs(/*in*/ pids, /*out*/ states);
+        }
+    }
+
+    /**
+     * For each PID in the given input array, write the current process state
+     * for that process into the output array, or -1 to indicate that no
+     * process with the given PID exists.
+     */
+    public void getProcessStatesForPIDs(/*in*/ int[] pids, /*out*/ int[] states) {
+        if (pids == null) {
+            throw new NullPointerException("pids");
+        } else if (states == null) {
+            throw new NullPointerException("states");
+        } else if (pids.length != states.length) {
+            throw new IllegalArgumentException("input and output arrays have different lengths!");
+        }
+
+        synchronized (mPidsSelfLocked) {
+            for (int i = 0; i < pids.length; i++) {
+                ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
+                states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT :
+                        pr.curProcState;
+            }
+        }
+    }
+
     // =========================================================
     // PERMISSIONS
     // =========================================================
@@ -7053,7 +7103,6 @@
                 return;
             }
 
-            final IPackageManager pm = AppGlobals.getPackageManager();
             final String authority = uri.getAuthority();
             final ProviderInfo pi = getProviderInfoLocked(authority, userId);
             if (pi == null) {
@@ -7822,6 +7871,18 @@
     }
 
     @Override
+    public void setTaskResizeable(int taskId, boolean resizeable) {
+        synchronized (this) {
+            TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            if (task == null) {
+                Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
+                return;
+            }
+            task.mResizeable = resizeable;
+        }
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filename) {
         if (!FileUtils.isValidExtFilename(filename)
                 || !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
@@ -8162,12 +8223,14 @@
     }
 
     @Override
-    public void resizeStack(int stackBoxId, Rect bounds) {
+    public void resizeStack(int stackId, Rect bounds) {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "resizeStackBox()");
+                "resizeStack()");
         long ident = Binder.clearCallingIdentity();
         try {
-            mWindowManager.resizeStack(stackBoxId, bounds);
+            synchronized (this) {
+                mStackSupervisor.resizeStackLocked(stackId, bounds);
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -10034,7 +10097,7 @@
                 }
                 final boolean translucentChanged = r.changeWindowTranslucency(true);
                 if (translucentChanged) {
-                    r.task.stack.releaseBackgroundResources();
+                    r.task.stack.releaseBackgroundResources(r);
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                 }
                 mWindowManager.setAppFullscreen(token, true);
@@ -10061,7 +10124,7 @@
                 }
                 final boolean translucentChanged = r.changeWindowTranslucency(false);
                 if (translucentChanged) {
-                    r.task.stack.convertToTranslucent(r);
+                    r.task.stack.convertActivityToTranslucent(r);
                 }
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                 mWindowManager.setAppFullscreen(token, false);
@@ -10578,9 +10641,68 @@
         }
     }
 
+    final class PreBootContinuation extends IIntentReceiver.Stub {
+        final Intent intent;
+        final Runnable onFinishCallback;
+        final ArrayList<ComponentName> doneReceivers;
+        final List<ResolveInfo> ris;
+        final int[] users;
+        int lastRi = -1;
+        int curRi = 0;
+        int curUser = 0;
+
+        PreBootContinuation(Intent _intent, Runnable _onFinishCallback,
+                ArrayList<ComponentName> _doneReceivers, List<ResolveInfo> _ris, int[] _users) {
+            intent = _intent;
+            onFinishCallback = _onFinishCallback;
+            doneReceivers = _doneReceivers;
+            ris = _ris;
+            users = _users;
+        }
+
+        void go() {
+            if (lastRi != curRi) {
+                ActivityInfo ai = ris.get(curRi).activityInfo;
+                ComponentName comp = new ComponentName(ai.packageName, ai.name);
+                intent.setComponent(comp);
+                doneReceivers.add(comp);
+                lastRi = curRi;
+                CharSequence label = ai.loadLabel(mContext.getPackageManager());
+                showBootMessage(mContext.getString(R.string.android_preparing_apk, label), false);
+            }
+            Slog.i(TAG, "Pre-boot of " + intent.getComponent().toShortString()
+                    + " for user " + users[curUser]);
+            EventLogTags.writeAmPreBoot(users[curUser], intent.getComponent().getPackageName());
+            broadcastIntentLocked(null, null, intent, null, this,
+                    0, null, null, null, AppOpsManager.OP_NONE,
+                    true, false, MY_PID, Process.SYSTEM_UID,
+                    users[curUser]);
+        }
+
+        public void performReceive(Intent intent, int resultCode,
+                String data, Bundle extras, boolean ordered,
+                boolean sticky, int sendingUser) {
+            curUser++;
+            if (curUser >= users.length) {
+                curUser = 0;
+                curRi++;
+                if (curRi >= ris.size()) {
+                    // All done sending broadcasts!
+                    if (onFinishCallback != null) {
+                        // The raw IIntentReceiver interface is called
+                        // with the AM lock held, so redispatch to
+                        // execute our code without the lock.
+                        mHandler.post(onFinishCallback);
+                    }
+                    return;
+                }
+            }
+            go();
+        }
+    }
+
     private boolean deliverPreBootCompleted(final Runnable onFinishCallback,
             ArrayList<ComponentName> doneReceivers, int userId) {
-        boolean waitingUpdate = false;
         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
         List<ResolveInfo> ris = null;
         try {
@@ -10588,71 +10710,51 @@
                     intent, null, 0, userId);
         } catch (RemoteException e) {
         }
-        if (ris != null) {
-            for (int i=ris.size()-1; i>=0; i--) {
-                if ((ris.get(i).activityInfo.applicationInfo.flags
-                        &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                    ris.remove(i);
-                }
+        if (ris == null) {
+            return false;
+        }
+        for (int i=ris.size()-1; i>=0; i--) {
+            if ((ris.get(i).activityInfo.applicationInfo.flags
+                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
+                ris.remove(i);
             }
-            intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+        }
+        intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
 
-            // For User 0, load the version number. When delivering to a new user, deliver
-            // to all receivers.
-            if (userId == UserHandle.USER_OWNER) {
-                ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
-                for (int i=0; i<ris.size(); i++) {
-                    ActivityInfo ai = ris.get(i).activityInfo;
-                    ComponentName comp = new ComponentName(ai.packageName, ai.name);
-                    if (lastDoneReceivers.contains(comp)) {
-                        // We already did the pre boot receiver for this app with the current
-                        // platform version, so don't do it again...
-                        ris.remove(i);
-                        i--;
-                        // ...however, do keep it as one that has been done, so we don't
-                        // forget about it when rewriting the file of last done receivers.
-                        doneReceivers.add(comp);
-                    }
-                }
-            }
-
-            // If primary user, send broadcast to all available users, else just to userId
-            final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
-                    : new int[] { userId };
-            for (int i = 0; i < ris.size(); i++) {
+        // For User 0, load the version number. When delivering to a new user, deliver
+        // to all receivers.
+        if (userId == UserHandle.USER_OWNER) {
+            ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+            for (int i=0; i<ris.size(); i++) {
                 ActivityInfo ai = ris.get(i).activityInfo;
                 ComponentName comp = new ComponentName(ai.packageName, ai.name);
-                doneReceivers.add(comp);
-                intent.setComponent(comp);
-                for (int j=0; j<users.length; j++) {
-                    IIntentReceiver finisher = null;
-                    // On last receiver and user, set up a completion callback
-                    if (i == ris.size() - 1 && j == users.length - 1 && onFinishCallback != null) {
-                        finisher = new IIntentReceiver.Stub() {
-                            public void performReceive(Intent intent, int resultCode,
-                                    String data, Bundle extras, boolean ordered,
-                                    boolean sticky, int sendingUser) {
-                                // The raw IIntentReceiver interface is called
-                                // with the AM lock held, so redispatch to
-                                // execute our code without the lock.
-                                mHandler.post(onFinishCallback);
-                            }
-                        };
-                    }
-                    Slog.i(TAG, "Sending system update to " + intent.getComponent()
-                            + " for user " + users[j]);
-                    broadcastIntentLocked(null, null, intent, null, finisher,
-                            0, null, null, null, AppOpsManager.OP_NONE,
-                            true, false, MY_PID, Process.SYSTEM_UID,
-                            users[j]);
-                    if (finisher != null) {
-                        waitingUpdate = true;
-                    }
+                if (false && lastDoneReceivers.contains(comp)) {
+                    // We already did the pre boot receiver for this app with the current
+                    // platform version, so don't do it again...
+                    ris.remove(i);
+                    i--;
+                    // ...however, do keep it as one that has been done, so we don't
+                    // forget about it when rewriting the file of last done receivers.
+                    doneReceivers.add(comp);
                 }
             }
         }
 
-        return waitingUpdate;
+        if (ris.size() <= 0) {
+            return false;
+        }
+
+        // If primary user, send broadcast to all available users, else just to userId
+        final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
+                : new int[] { userId };
+        if (users.length <= 0) {
+            return false;
+        }
+
+        PreBootContinuation cont = new PreBootContinuation(intent, onFinishCallback, doneReceivers,
+                ris, users);
+        cont.go();
+        return true;
     }
 
     public void systemReady(final Runnable goingCallback) {
@@ -10687,10 +10789,10 @@
                         synchronized (ActivityManagerService.this) {
                             mDidUpdate = true;
                         }
-                        writeLastDonePreBootReceivers(doneReceivers);
                         showBootMessage(mContext.getText(
                                 R.string.android_upgrading_complete),
                                 false);
+                        writeLastDonePreBootReceivers(doneReceivers);
                         systemReady(goingCallback);
                     }
                 }, doneReceivers, UserHandle.USER_OWNER);
@@ -13858,9 +13960,14 @@
             memInfo.readMemInfo();
             if (nativeProcTotalPss > 0) {
                 synchronized (this) {
-                    mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
-                            memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
-                            memInfo.getKernelUsedSizeKb(), nativeProcTotalPss);
+                    final long cachedKb = memInfo.getCachedSizeKb();
+                    final long freeKb = memInfo.getFreeSizeKb();
+                    final long zramKb = memInfo.getZramTotalSizeKb();
+                    final long kernelKb = memInfo.getKernelUsedSizeKb();
+                    EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+                            kernelKb*1024, nativeProcTotalPss*1024);
+                    mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+                            nativeProcTotalPss);
                 }
             }
             if (!brief) {
@@ -14994,30 +15101,6 @@
     // BROADCASTS
     // =========================================================
 
-    private final List getStickiesLocked(String action, IntentFilter filter,
-            List cur, int userId) {
-        final ContentResolver resolver = mContext.getContentResolver();
-        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
-        if (stickies == null) {
-            return cur;
-        }
-        final ArrayList<Intent> list = stickies.get(action);
-        if (list == null) {
-            return cur;
-        }
-        int N = list.size();
-        for (int i=0; i<N; i++) {
-            Intent intent = list.get(i);
-            if (filter.match(resolver, intent, true, TAG) >= 0) {
-                if (cur == null) {
-                    cur = new ArrayList<Intent>();
-                }
-                cur.add(intent);
-            }
-        }
-        return cur;
-    }
-
     boolean isPendingBroadcastProcessLocked(int pid) {
         return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
                 || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid);
@@ -15042,10 +15125,11 @@
     public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
         enforceNotIsolatedCaller("registerReceiver");
+        ArrayList<Intent> stickyIntents = null;
+        ProcessRecord callerApp = null;
         int callingUid;
         int callingPid;
         synchronized(this) {
-            ProcessRecord callerApp = null;
             if (caller != null) {
                 callerApp = getRecordForAppLocked(caller);
                 if (callerApp == null) {
@@ -15068,39 +15152,66 @@
                 callingPid = Binder.getCallingPid();
             }
 
-            userId = this.handleIncomingUser(callingPid, callingUid, userId,
+            userId = handleIncomingUser(callingPid, callingUid, userId,
                     true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
 
-            List allSticky = null;
+            Iterator<String> actions = filter.actionsIterator();
+            if (actions == null) {
+                ArrayList<String> noAction = new ArrayList<String>(1);
+                noAction.add(null);
+                actions = noAction.iterator();
+            }
 
-            // Look for any matching sticky broadcasts...
-            Iterator actions = filter.actionsIterator();
-            if (actions != null) {
-                while (actions.hasNext()) {
-                    String action = (String)actions.next();
-                    allSticky = getStickiesLocked(action, filter, allSticky,
-                            UserHandle.USER_ALL);
-                    allSticky = getStickiesLocked(action, filter, allSticky,
-                            UserHandle.getUserId(callingUid));
+            // Collect stickies of users
+            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
+            while (actions.hasNext()) {
+                String action = actions.next();
+                for (int id : userIds) {
+                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
+                    if (stickies != null) {
+                        ArrayList<Intent> intents = stickies.get(action);
+                        if (intents != null) {
+                            if (stickyIntents == null) {
+                                stickyIntents = new ArrayList<Intent>();
+                            }
+                            stickyIntents.addAll(intents);
+                        }
+                    }
                 }
-            } else {
-                allSticky = getStickiesLocked(null, filter, allSticky,
-                        UserHandle.USER_ALL);
-                allSticky = getStickiesLocked(null, filter, allSticky,
-                        UserHandle.getUserId(callingUid));
             }
+        }
 
-            // The first sticky in the list is returned directly back to
-            // the client.
-            Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
-
-            if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter
-                    + ": " + sticky);
-
-            if (receiver == null) {
-                return sticky;
+        ArrayList<Intent> allSticky = null;
+        if (stickyIntents != null) {
+            final ContentResolver resolver = mContext.getContentResolver();
+            // Look for any matching sticky broadcasts...
+            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
+                Intent intent = stickyIntents.get(i);
+                // If intent has scheme "content", it will need to acccess
+                // provider that needs to lock mProviderMap in ActivityThread
+                // and also it may need to wait application response, so we
+                // cannot lock ActivityManagerService here.
+                if (filter.match(resolver, intent, true, TAG) >= 0) {
+                    if (allSticky == null) {
+                        allSticky = new ArrayList<Intent>();
+                    }
+                    allSticky.add(intent);
+                }
             }
+        }
 
+        // The first sticky in the list is returned directly back to the client.
+        Intent sticky = allSticky != null ? allSticky.get(0) : null;
+        if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter + ": " + sticky);
+        if (receiver == null) {
+            return sticky;
+        }
+
+        synchronized (this) {
+            if (callerApp != null && callerApp.pid == 0) {
+                // Caller already died
+                return null;
+            }
             ReceiverList rl
                 = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
             if (rl == null) {
@@ -15739,14 +15850,14 @@
                     callerPackage, callingPid, callingUid, resolvedType,
                     requiredPermission, appOp, receivers, resultTo, resultCode,
                     resultData, map, ordered, sticky, false, userId);
+
             if (DEBUG_BROADCAST) Slog.v(
                     TAG, "Enqueueing ordered broadcast " + r
                     + ": prev had " + queue.mOrderedBroadcasts.size());
-            if (DEBUG_BROADCAST) {
-                int seq = r.intent.getIntExtra("seq", -1);
-                Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
-            }
-            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
+            if (DEBUG_BROADCAST) Slog.i(
+                    TAG, "Enqueueing broadcast " + r.intent.getAction());
+
+            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
             if (!replaced) {
                 queue.enqueueOrderedBroadcastLocked(r);
                 queue.scheduleBroadcastsLocked();
@@ -16077,6 +16188,15 @@
         return mStackSupervisor.getFocusedStack();
     }
 
+    @Override
+    public int getFocusedStackId() throws RemoteException {
+        ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            return focusedStack.getStackId();
+        }
+        return -1;
+    }
+
     public Configuration getConfiguration() {
         Configuration ci;
         synchronized(this) {
@@ -16253,7 +16373,8 @@
      */
     private static final boolean shouldShowDialogs(Configuration config) {
         return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
-                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH);
+                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+                && config.navigation == Configuration.NAVIGATION_NONAV);
     }
 
     /**
@@ -17054,6 +17175,7 @@
      * Record new PSS sample for a process.
      */
     void recordPssSample(ProcessRecord proc, int procState, long pss, long uss, long now) {
+        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss*1024, uss*1024);
         proc.lastPssTime = now;
         proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
         if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
@@ -18307,8 +18429,8 @@
         }
     }
 
-    private Set getProfileIdsLocked(int userId) {
-        Set userIds = new HashSet<Integer>();
+    private Set<Integer> getProfileIdsLocked(int userId) {
+        Set<Integer> userIds = new HashSet<Integer>();
         final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
                 userId, false /* enabledOnly */);
         for (UserInfo user : profiles) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6573521..b3f47e9 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -75,7 +75,6 @@
     static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
     final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
 
-    private static final String TAG_ACTIVITY = "activity";
     private static final String ATTR_ID = "id";
     private static final String TAG_INTENT = "intent";
     private static final String ATTR_USERID = "user_id";
@@ -127,6 +126,10 @@
     long pauseTime;         // last time we started pausing the activity
     long launchTickTime;    // base time for launch tick messages
     Configuration configuration; // configuration activity was last running in
+    // Overridden configuration by the activity stack
+    // WARNING: Reference points to {@link ActivityStack#mOverrideConfig}, so its internal state
+    // should never be altered directly.
+    Configuration stackConfigOverride;
     CompatibilityInfo compat;// last used compatibility mode
     ActivityRecord resultTo; // who started this entry, so will get our reply
     final String resultWho; // additional identifier for use by resultTo.
@@ -204,6 +207,7 @@
                 pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
                 pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
         pw.print(prefix); pw.print("config="); pw.println(configuration);
+        pw.print(prefix); pw.print("stackConfigOverride="); pw.println(stackConfigOverride);
         if (resultTo != null || resultWho != null) {
             pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
                     pw.print(" resultWho="); pw.print(resultWho);
@@ -394,6 +398,8 @@
         resolvedType = _resolvedType;
         componentSpecified = _componentSpecified;
         configuration = _configuration;
+        stackConfigOverride = (container != null)
+                ? container.mStack.mOverrideConfig : Configuration.EMPTY;
         resultTo = _resultTo;
         resultWho = _resultWho;
         requestCode = _reqCode;
@@ -1066,10 +1072,7 @@
 
     static ActivityRecord isInStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.forToken(token);
-        if (r != null) {
-            return r.task.stack.isInStackLocked(token);
-        }
-        return null;
+        return (r != null) ? r.task.stack.isInStackLocked(r) : null;
     }
 
     static ActivityStack getStackLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 13df444..19f6e5a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -220,6 +220,9 @@
      */
     boolean mConfigWillChange;
 
+    // Whether or not this stack covers the entire screen; by default stacks are full screen
+    boolean mFullscreen = true;
+
     long mLaunchStartTime = 0;
     long mFullyDrawnStartTime = 0;
 
@@ -235,6 +238,11 @@
     /** Run all ActivityStacks through this */
     final ActivityStackSupervisor mStackSupervisor;
 
+    Configuration mOverrideConfig;
+    /** True if the stack was forced to full screen because {@link TaskRecord#mResizeable} is false
+     * and the stack was previously resized. */
+    private boolean mForcedFullscreen = false;
+
     static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
     static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
     static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -348,6 +356,7 @@
         mStackId = activityContainer.mStackId;
         mCurrentUser = mService.mCurrentUserId;
         mRecentTasks = recentTasks;
+        mOverrideConfig = Configuration.EMPTY;
     }
 
     /**
@@ -450,13 +459,18 @@
 
     ActivityRecord isInStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.forToken(token);
-        if (r != null) {
-            final TaskRecord task = r.task;
-            if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
-                if (task.stack != this) Slog.w(TAG,
+        return isInStackLocked(r);
+    }
+
+    ActivityRecord isInStackLocked(ActivityRecord r) {
+        if (r == null) {
+            return null;
+        }
+        final TaskRecord task = r.task;
+        if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
+            if (task.stack != this) Slog.w(TAG,
                     "Illegal state! task does not point to stack it is in.");
-                return r;
-            }
+            return r;
         }
         return null;
     }
@@ -1117,7 +1131,8 @@
 
         final int numStacks = mStacks.size();
         while (stackNdx < numStacks) {
-            tasks = mStacks.get(stackNdx).mTaskHistory;
+            ActivityStack historyStack = mStacks.get(stackNdx);
+            tasks = historyStack.mTaskHistory;
             final int numTasks = tasks.size();
             while (taskNdx < numTasks) {
                 activities = tasks.get(taskNdx).mActivities;
@@ -1125,7 +1140,7 @@
                 while (activityNdx < numActivities) {
                     final ActivityRecord activity = activities.get(activityNdx);
                     if (!activity.finishing) {
-                        return activity.fullscreen ? null : activity;
+                        return historyStack.mFullscreen && activity.fullscreen ? null : activity;
                     }
                     ++activityNdx;
                 }
@@ -1141,7 +1156,7 @@
 
     // Checks if any of the stacks above this one has a fullscreen activity behind it.
     // If so, this stack is hidden, otherwise it is visible.
-    private boolean isStackVisible() {
+    private boolean isStackVisibleLocked() {
         if (!isAttached()) {
             return false;
         }
@@ -1156,11 +1171,18 @@
          * wallpaper to be shown behind it.
          */
         for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
-            final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
-            for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
+            ActivityStack stack = mStacks.get(i);
+            // stack above isn't full screen, so, we assume we're still visible. at some point
+            // we should look at the stack bounds to see if we're occluded even if the stack
+            // isn't fullscreen
+            if (!stack.mFullscreen) {
+                continue;
+            }
+            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 final TaskRecord task = tasks.get(taskNdx);
                 final ArrayList<ActivityRecord> activities = task.mActivities;
-                for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
+                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                     final ActivityRecord r = activities.get(activityNdx);
 
                     // Conditions for an activity to obscure the stack we're
@@ -1206,7 +1228,7 @@
         // If the top activity is not fullscreen, then we need to
         // make sure any activities under it are now visible.
         boolean aboveTop = true;
-        boolean behindFullscreen = !isStackVisible();
+        boolean behindFullscreen = !isStackVisibleLocked();
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1329,7 +1351,7 @@
                                     // This case created for transitioning activities from
                                     // translucent to opaque {@link Activity#convertToOpaque}.
                                     if (getVisibleBehindActivity() == r) {
-                                        releaseBackgroundResources();
+                                        releaseBackgroundResources(r);
                                     } else {
                                         if (!mStackSupervisor.mStoppingActivities.contains(r)) {
                                             mStackSupervisor.mStoppingActivities.add(r);
@@ -1361,7 +1383,7 @@
         }
     }
 
-    void convertToTranslucent(ActivityRecord r) {
+    void convertActivityToTranslucent(ActivityRecord r) {
         mTranslucentActivityWaiting = r;
         mUndrawnActivitiesBelowTopTranslucent.clear();
         mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
@@ -1897,9 +1919,31 @@
         return true;
     }
 
+    private TaskRecord getNextTask(TaskRecord targetTask) {
+        final int index = mTaskHistory.indexOf(targetTask);
+        if (index >= 0) {
+            final int numTasks = mTaskHistory.size();
+            for (int i = index + 1; i < numTasks; ++i) {
+                TaskRecord task = mTaskHistory.get(i);
+                if (task.userId == targetTask.userId) {
+                    return task;
+                }
+            }
+        }
+        return null;
+    }
+
     private void insertTaskAtTop(TaskRecord task) {
+        // If the moving task is over home stack, transfer its return type to next task
+        if (task.isOverHomeStack()) {
+            final TaskRecord nextTask = getNextTask(task);
+            if (nextTask != null) {
+                nextTask.setTaskToReturnTo(task.getTaskToReturnTo());
+            }
+        }
+
         // If this is being moved to the top by another activity or being launched from the home
-        // activity, set mOnTopOfHome accordingly.
+        // activity, set mTaskToReturnTo accordingly.
         if (isOnHomeDisplay()) {
             ActivityStack lastStack = mStackSupervisor.getLastStack();
             final boolean fromHome = lastStack.isHomeStack();
@@ -2195,7 +2239,7 @@
                 }
 
                 final int targetTaskId = targetTask.taskId;
-                mWindowManager.setAppGroupId(target.appToken, targetTaskId);
+                mWindowManager.setAppTask(target.appToken, targetTaskId);
 
                 boolean noOptions = canMoveOptions;
                 final int start = replyChainEnd < 0 ? i : replyChainEnd;
@@ -2220,7 +2264,7 @@
                     p.setTask(targetTask, null);
                     targetTask.addActivityAtBottom(p);
 
-                    mWindowManager.setAppGroupId(p.appToken, targetTaskId);
+                    mWindowManager.setAppTask(p.appToken, targetTaskId);
                 }
 
                 mWindowManager.moveTaskToBottom(targetTaskId);
@@ -2360,7 +2404,7 @@
                                 new RuntimeException("here").fillInStackTrace());
                         if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos
                                 + " in to resetting task " + task);
-                        mWindowManager.setAppGroupId(p.appToken, taskId);
+                        mWindowManager.setAppTask(p.appToken, taskId);
                     }
                     mWindowManager.moveTaskToTop(taskId);
                     if (VALIDATE_TOKENS) {
@@ -3262,7 +3306,7 @@
             }
             if (DEBUG_CONTAINERS) Slog.d(TAG, "activityDestroyedLocked: r=" + r);
 
-            if (isInStackLocked(token) != null) {
+            if (isInStackLocked(r) != null) {
                 if (r.state == ActivityState.DESTROYING) {
                     cleanUpActivityLocked(r, true, false);
                     removeActivityFromHistoryLocked(r, reason);
@@ -3274,10 +3318,9 @@
         }
     }
 
-    void releaseBackgroundResources() {
+    void releaseBackgroundResources(ActivityRecord r) {
         if (hasVisibleBehindActivity() &&
                 !mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
-            final ActivityRecord r = getVisibleBehindActivity();
             if (r == topRunningActivityLocked(null)) {
                 // Don't release the top activity if it has requested to run behind the next
                 // activity.
@@ -3470,7 +3513,7 @@
         }
     }
 
-    final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options,
+    final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, Bundle options,
             String reason) {
         if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
 
@@ -3478,8 +3521,7 @@
         final int index = mTaskHistory.indexOf(tr);
         if (numTasks == 0 || index < 0)  {
             // nothing to do!
-            if (source != null &&
-                    (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+            if (noAnimation) {
                 ActivityOptions.abort(options);
             } else {
                 updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
@@ -3493,8 +3535,7 @@
         moveToFront(reason);
 
         if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
-        if (source != null &&
-                (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+        if (noAnimation) {
             mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
             ActivityRecord r = topRunningActivityLocked(null);
             if (r != null) {
@@ -3561,6 +3602,15 @@
         if (DEBUG_TRANSITION) Slog.v(TAG,
                 "Prepare to back transition: task=" + taskId);
 
+        boolean prevIsHome = false;
+        if (tr.isOverHomeStack()) {
+            final TaskRecord nextTask = getNextTask(tr);
+            if (nextTask != null) {
+                nextTask.setTaskToReturnTo(tr.getTaskToReturnTo());
+            } else {
+                prevIsHome = true;
+            }
+        }
         mTaskHistory.remove(tr);
         mTaskHistory.add(0, tr);
         updateTaskMovement(tr, false);
@@ -3587,7 +3637,8 @@
         }
 
         final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
-        if (task == tr && tr.isOverHomeStack() || numTasks <= 1 && isOnHomeDisplay()) {
+        if (prevIsHome || task == tr && tr.isOverHomeStack()
+                || numTasks <= 1 && isOnHomeDisplay()) {
             if (!mService.mBooting && !mService.mBooted) {
                 // Not ready yet!
                 return false;
@@ -3630,10 +3681,30 @@
         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                 "Ensuring correct configuration: " + r);
 
+        // Make sure the current stack override configuration is supported by the top task
+        // before continuing.
+        final TaskRecord topTask = topTask();
+        if (topTask != null && ((topTask.mResizeable && mForcedFullscreen)
+                    || (!topTask.mResizeable && !mFullscreen))) {
+            final boolean prevFullscreen = mFullscreen;
+            final Configuration newOverrideConfig =
+                    mWindowManager.forceStackToFullscreen(mStackId, !topTask.mResizeable);
+            updateOverrideConfiguration(newOverrideConfig);
+            mForcedFullscreen = !prevFullscreen && mFullscreen;
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Updated stack config to support task=" + topTask
+                            + " resizeable=" + topTask.mResizeable
+                            + " mForcedFullscreen=" + mForcedFullscreen
+                            + " prevFullscreen=" + prevFullscreen
+                            + " mFullscreen=" + mFullscreen);
+        }
+
         // Short circuit: if the two configurations are the exact same
         // object (the common case), then there is nothing to do.
         Configuration newConfig = mService.mConfiguration;
-        if (r.configuration == newConfig && !r.forceNewConfig) {
+        if (r.configuration == newConfig
+                && r.stackConfigOverride == mOverrideConfig
+                && !r.forceNewConfig) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                     "Configuration unchanged in " + r);
             return true;
@@ -3649,14 +3720,22 @@
 
         // Okay we now are going to make this activity have the new config.
         // But then we need to figure out how it needs to deal with that.
-        Configuration oldConfig = r.configuration;
+        final Configuration oldConfig = r.configuration;
+        final Configuration oldStackOverride = r.stackConfigOverride;
         r.configuration = newConfig;
+        r.stackConfigOverride = mOverrideConfig;
 
         // Determine what has changed.  May be nothing, if this is a config
         // 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.
-        final int changes = oldConfig.diff(newConfig);
+        int stackChanges = oldStackOverride.diff(mOverrideConfig);
+        if (stackChanges == 0 && !oldStackOverride.equals(mOverrideConfig)) {
+            // Assume size change if diff didn't report any changes,
+            // but configurations are not equal.
+            stackChanges = ActivityInfo.CONFIG_SCREEN_SIZE;
+        }
+        final int changes = oldConfig.diff(newConfig) | stackChanges;
         if (changes == 0 && !r.forceNewConfig) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                     "Configuration no differences in " + r);
@@ -3760,8 +3839,9 @@
                     (andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
                     + r);
             r.forceNewConfig = false;
-            r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents,
-                    changes, !andResume, new Configuration(mService.mConfiguration));
+            r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
+                    !andResume, new Configuration(mService.mConfiguration),
+                    new Configuration(mOverrideConfig));
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
@@ -4094,7 +4174,7 @@
     }
 
     ArrayList<TaskRecord> getAllTasks() {
-        return new ArrayList<TaskRecord>(mTaskHistory);
+        return new ArrayList<>(mTaskHistory);
     }
 
     void addTask(final TaskRecord task, final boolean toTop, boolean moving) {
@@ -4122,4 +4202,14 @@
         return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
                 + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
     }
+
+    boolean updateOverrideConfiguration(Configuration newConfig) {
+        Configuration oldConfig = mOverrideConfig;
+        mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig;
+        // We override the configuration only when the stack's dimensions are different from
+        // the display. In this manner, we know that if the override configuration is empty,
+        // the stack is necessarily full screen.
+        mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
+        return !mOverrideConfig.equals(oldConfig);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b7728b3..58104a8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -63,6 +63,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.display.DisplayManagerGlobal;
@@ -874,8 +875,7 @@
             } else {
                 stack = container.mStack;
             }
-            stack.mConfigWillChange = config != null
-                    && mService.mConfiguration.diff(config) != 0;
+            stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
             if (DEBUG_CONFIGURATION) Slog.v(TAG,
                     "Starting activity when config will change = " + stack.mConfigWillChange);
 
@@ -1181,9 +1181,9 @@
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
-                    r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,
-                    r.icicle, r.persistentState, results, newIntents, !andResume,
-                    mService.isNextTransitionForward(), profilerInfo);
+                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
+                    r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
+                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
 
             if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Note that the package
@@ -1596,7 +1596,7 @@
         }
     }
 
-    final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
+    final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
             boolean doResume, Bundle options, TaskRecord inTask) {
         final Intent intent = r.intent;
@@ -1807,6 +1807,7 @@
         ActivityStack targetStack;
 
         intent.setFlags(launchFlags);
+        final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
 
         // We may want to try to place the new activity in to an existing task.  We always
         // do this if the target activity is singleTask or singleInstance; we will also do
@@ -1869,8 +1870,8 @@
                                 intentActivity.setTaskToAffiliateWith(sourceRecord.task);
                             }
                             movedHome = true;
-                            targetStack.moveTaskToFrontLocked(intentActivity.task, r, options,
-                                    "bringingFoundTaskToFront");
+                            targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
+                                    options, "bringingFoundTaskToFront");
                             if ((launchFlags &
                                     (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
                                     == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
@@ -2100,7 +2101,8 @@
             targetStack.moveToFront("sourceStackToFront");
             final TaskRecord topTask = targetStack.topTask();
             if (topTask != sourceTask) {
-                targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront");
+                targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
+                        "sourceTaskToFront");
             }
             if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
                 // In this case, we are adding the activity to an existing
@@ -2154,7 +2156,7 @@
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
             targetStack = inTask.stack;
-            targetStack.moveTaskToFrontLocked(inTask, r, options, "inTaskToFront");
+            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, "inTaskToFront");
 
             // Check whether we should actually launch the new activity in to the task,
             // or just reuse the current activity on top.
@@ -2519,7 +2521,7 @@
             // we'll just indicate that this task returns to the home task.
             task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
         }
-        task.stack.moveTaskToFrontLocked(task, null, options, reason);
+        task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options, reason);
         if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
                 + task.stack);
     }
@@ -2596,6 +2598,33 @@
         }
     }
 
+    void resizeStackLocked(int stackId, Rect bounds) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+            return;
+        }
+
+        final ActivityRecord r = stack.topRunningActivityLocked(null);
+        if (r != null && !r.task.mResizeable) {
+            Slog.w(TAG, "resizeStack: top task " + r.task + " not resizeable.");
+            return;
+        }
+
+        final Configuration overrideConfig = mWindowManager.resizeStack(stackId, bounds);
+        if (stack.updateOverrideConfiguration(overrideConfig)) {
+            if (r != null) {
+                final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+                // And we need to make sure at this point that all other activities
+                // are made visible with the correct configuration.
+                ensureActivitiesVisibleLocked(r, 0);
+                if (!updated) {
+                    resumeTopActivitiesLocked(stack, null, null);
+                }
+            }
+        }
+    }
+
     ActivityStack createStackOnDisplay(int stackId, int displayId) {
         ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
         if (activityDisplay == null) {
@@ -3729,6 +3758,13 @@
         }
 
         @Override
+        public int getStackId() {
+            synchronized (mService) {
+                return mStackId;
+            }
+        }
+
+        @Override
         public boolean injectEvent(InputEvent event) {
             final long origId = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 9b7d0b2..7ab3794 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -513,11 +513,7 @@
                 }
             }
             try {
-                if (DEBUG_BROADCAST_LIGHT) {
-                    int seq = r.intent.getIntExtra("seq", -1);
-                    Slog.i(TAG, "Delivering to " + filter
-                            + " (seq=" + seq + "): " + r);
-                }
+                if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG, "Delivering to " + filter + " : " + r);
                 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                     new Intent(r.intent), r.resultCode, r.resultData,
                     r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -662,12 +658,9 @@
                     // result if requested...
                     if (r.resultTo != null) {
                         try {
-                            if (DEBUG_BROADCAST) {
-                                int seq = r.intent.getIntExtra("seq", -1);
-                                Slog.i(TAG, "Finishing broadcast ["
-                                        + mQueueName + "] " + r.intent.getAction()
-                                        + " seq=" + seq + " app=" + r.callerApp);
-                            }
+                            if (DEBUG_BROADCAST) Slog.i(TAG,
+                                    "Finishing broadcast [" + mQueueName + "] "
+                                    + r.intent.getAction() + " app=" + r.callerApp);
                             performReceiveLocked(r.callerApp, r.resultTo,
                                 new Intent(r.intent), r.resultCode,
                                 r.resultData, r.resultExtras, false, false, r.userId);
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index c376744..9a645df 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -95,3 +95,11 @@
 
 # Home Stack brought to front or rear
 30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3)
+
+# Running pre boot receiver
+30045 am_pre_boot (User|1|5),(Package|3)
+
+# Report collection of global memory state
+30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2)
+# Report collection of memory used by a process
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2)
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 3011148..76bb498 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -79,6 +79,7 @@
     private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
     private static final String ATTR_CALLING_UID = "calling_uid";
     private static final String ATTR_CALLING_PACKAGE = "calling_package";
+    private static final String ATTR_RESIZEABLE = "resizeable";
 
     private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
 
@@ -109,10 +110,12 @@
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
-    int creatorUid;         // The app uid that originally created the task
 
     int numFullscreen;      // Number of fullscreen activities.
 
+    boolean mResizeable;    // Activities in the task resizeable. Based on the resizable setting of
+                            // the root activity.
+
     // This represents the last resolved activity values for this task
     // NOTE: This value needs to be persisted with each task
     TaskDescription lastTaskDescription = new TaskDescription();
@@ -176,7 +179,7 @@
         voiceSession = _voiceSession;
         voiceInteractor = _voiceInteractor;
         isAvailable = true;
-        mActivities = new ArrayList<ActivityRecord>();
+        mActivities = new ArrayList<>();
         setIntent(_intent, info);
     }
 
@@ -191,7 +194,7 @@
         voiceSession = null;
         voiceInteractor = null;
         isAvailable = true;
-        mActivities = new ArrayList<ActivityRecord>();
+        mActivities = new ArrayList<>();
         setIntent(_intent, info);
 
         taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
@@ -210,15 +213,15 @@
         mCallingPackage = info.packageName;
     }
 
-    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
-            String _affinity, String _rootAffinity, ComponentName _realActivity,
-            ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
-            boolean _askedCompatMode, int _taskType, int _userId, int _effectiveUid,
-            String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
-            long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
-            TaskDescription _lastTaskDescription, int taskAffiliation,
-            int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid,
-            String callingPackage) {
+    private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
+            Intent _affinityIntent, String _affinity, String _rootAffinity,
+            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
+            boolean _autoRemoveRecents, boolean _askedCompatMode, int _taskType, int _userId,
+            int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
+            long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
+            boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
+            int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
+            int callingUid, String callingPackage, boolean resizeable) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -227,7 +230,7 @@
         intent = _intent;
         affinityIntent = _affinityIntent;
         affinity = _affinity;
-        rootAffinity = _affinity;
+        rootAffinity = _rootAffinity;
         voiceSession = null;
         voiceInteractor = null;
         realActivity = _realActivity;
@@ -253,6 +256,7 @@
         mNextAffiliateTaskId = nextTaskId;
         mCallingUid = callingUid;
         mCallingPackage = callingPackage;
+        mResizeable = resizeable;
     }
 
     void touchActiveTime() {
@@ -352,6 +356,7 @@
         } else {
             autoRemoveRecents = false;
         }
+        mResizeable = info.resizeable;
     }
 
     void setTaskToReturnTo(int taskToReturnTo) {
@@ -850,6 +855,7 @@
         out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
         out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
+        out.attribute(null, ATTR_RESIZEABLE, String.valueOf(mResizeable));
 
         if (affinityIntent != null) {
             out.startTag(null, TAG_AFFINITYINTENT);
@@ -911,6 +917,7 @@
         int nextTaskId = INVALID_TASK_ID;
         int callingUid = -1;
         String callingPackage = "";
+        boolean resizeable = false;
 
         for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
             final String attrName = in.getAttributeName(attrNdx);
@@ -964,6 +971,8 @@
                 callingUid = Integer.valueOf(attrValue);
             } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
                 callingPackage = attrValue;
+            } else if (ATTR_RESIZEABLE.equals(attrName)) {
+                resizeable = Boolean.valueOf(attrValue);
             } else {
                 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
             }
@@ -993,13 +1002,11 @@
                 }
             }
         }
-
         if (!hasRootAffinity) {
             rootAffinity = affinity;
         } else if ("@".equals(rootAffinity)) {
             rootAffinity = null;
         }
-
         if (effectiveUid <= 0) {
             Intent checkIntent = intent != null ? intent : affinityIntent;
             effectiveUid = 0;
@@ -1025,7 +1032,7 @@
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
-                callingUid, callingPackage);
+                callingUid, callingPackage, resizeable);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
             activities.get(activityNdx).task = task;
@@ -1121,6 +1128,7 @@
             pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription);
         }
         pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
+                pw.print(" mResizeable="); pw.print(mResizeable);
                 pw.print(" firstActiveTime="); pw.print(lastActiveTime);
                 pw.print(" lastActiveTime="); pw.print(lastActiveTime);
                 pw.print(" (inactive for ");
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index af5ed83..87f78c1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -28,6 +28,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.net.wifi.WifiInfo;
@@ -163,6 +164,7 @@
     /**
      * Force evaluation even if it has succeeded in the past.
      * arg1 = UID responsible for requesting this reeval.  Will be billed for data.
+     * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.)
      */
     public static final int CMD_FORCE_REEVALUATION = BASE + 8;
 
@@ -212,11 +214,14 @@
 
     // Negative values disable reevaluation.
     private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay";
-    // Default to 5s reevaluation delay.
+    // When connecting, attempt to validate 3 times, pausing 5s between them.
     private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
-    private static final int MAX_RETRIES = 10;
-    // Between groups of MAX_RETRIES evaluation attempts, pause 10 mins in hopes ISP outage passes.
+    private static final int INITIAL_ATTEMPTS = 3;
+    // If a network is not validated, make one attempt every 10 mins to see if it starts working.
     private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
+    private static final int PERIODIC_ATTEMPTS = 1;
+    // When an application calls reportBadNetwork, only make one attempt.
+    private static final int REEVALUATE_ATTEMPTS = 1;
     private final int mReevaluateDelayMs;
     private int mReevaluateToken = 0;
     private static final int INVALID_UID = -1;
@@ -236,6 +241,14 @@
     // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
     private boolean mUserDoesNotWant = false;
 
+    // How many times we should attempt validation. Only checked in EvaluatingState; must be set
+    // before entering EvaluatingState. Note that whatever code causes us to transition to
+    // EvaluatingState last decides how many attempts will be made, so if one codepath were to
+    // enter EvaluatingState with a specific number of attempts, and then another were to enter it
+    // with a different number of attempts, the second number would be used. This is not currently
+    // a problem because EvaluatingState is not reentrant.
+    private int mMaxAttempts;
+
     public boolean systemReady = false;
 
     private final State mDefaultState = new DefaultState();
@@ -305,6 +318,7 @@
                     return HANDLED;
                 case CMD_NETWORK_CONNECTED:
                     if (DBG) log("Connected");
+                    mMaxAttempts = INITIAL_ATTEMPTS;
                     transitionTo(mEvaluatingState);
                     return HANDLED;
                 case CMD_NETWORK_DISCONNECTED:
@@ -318,6 +332,7 @@
                 case CMD_FORCE_REEVALUATION:
                     if (DBG) log("Forcing reevaluation");
                     mUidResponsibleForReeval = message.arg1;
+                    mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS;
                     transitionTo(mEvaluatingState);
                     return HANDLED;
                 case CMD_CAPTIVE_PORTAL_APP_FINISHED:
@@ -347,7 +362,10 @@
         public void enter() {
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
-            if (!mUserDoesNotWant) sendMessageDelayed(CMD_FORCE_REEVALUATION, REEVALUATE_PAUSE_MS);
+            if (!mUserDoesNotWant) {
+                sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */,
+                        PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS);
+            }
         }
 
         @Override
@@ -413,11 +431,11 @@
     // Being in the EvaluatingState State indicates the Network is being evaluated for internet
     // connectivity.
     private class EvaluatingState extends State {
-        private int mRetries;
+        private int mAttempt;
 
         @Override
         public void enter() {
-            mRetries = 0;
+            mAttempt = 1;
             sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
             if (mUidResponsibleForReeval != INVALID_UID) {
                 TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
@@ -454,18 +472,18 @@
                         transitionTo(mValidatedState);
                         return HANDLED;
                     }
-                    // Note: This call to isCaptivePortal() could take minutes.  Resolving the
-                    // server's IP addresses could hit the DNS timeout and attempting connections
-                    // to each of the server's several (e.g. 11) IP addresses could each take
-                    // SOCKET_TIMEOUT_MS.  During this time this StateMachine will be unresponsive.
-                    // isCaptivePortal() could be executed on another Thread if this is found to
-                    // cause problems.
+                    // Note: This call to isCaptivePortal() could take up to a minute. Resolving the
+                    // server's IP addresses could hit the DNS timeout, and attempting connections
+                    // to each of the server's several IP addresses (currently one IPv4 and one
+                    // IPv6) could each take SOCKET_TIMEOUT_MS.  During this time this StateMachine
+                    // will be unresponsive. isCaptivePortal() could be executed on another Thread
+                    // if this is found to cause problems.
                     int httpResponseCode = isCaptivePortal();
                     if (httpResponseCode == 204) {
                         transitionTo(mValidatedState);
                     } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
                         transitionTo(mCaptivePortalState);
-                    } else if (++mRetries > MAX_RETRIES) {
+                    } else if (++mAttempt > mMaxAttempts) {
                         transitionTo(mOfflineState);
                     } else if (mReevaluateDelayMs >= 0) {
                         Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
@@ -639,12 +657,36 @@
         int httpResponseCode = 599;
         try {
             URL url = new URL("http", mServer, "/generate_204");
+            // On networks with a PAC instead of fetching a URL that should result in a 204
+            // reponse, we instead simply fetch the PAC script.  This is done for a few reasons:
+            // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
+            //    until something like https://android-review.googlesource.com/#/c/115180/ lands.
+            //    Network.openConnection() will ignore network-specific PACs and instead fetch
+            //    using NO_PROXY.  If a PAC is in place, the only fetch we know will succeed with
+            //    NO_PROXY is the fetch of the PAC itself.
+            // 2. To proxy the generate_204 fetch through a PAC would require a number of things
+            //    happen before the fetch can commence, namely:
+            //        a) the PAC script be fetched
+            //        b) a PAC script resolver service be fired up and resolve mServer
+            //    Network validation could be delayed until these prerequisities are satisifed or
+            //    could simply be left to race them.  Neither is an optimal solution.
+            // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
+            //    fact block fetching of the generate_204 URL which would lead to false negative
+            //    results for network validation.
+            boolean fetchPac = false;
+            {
+                final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
+                if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
+                    url = new URL(proxyInfo.getPacFileUrl().toString());
+                    fetchPac = true;
+                }
+            }
             if (DBG) {
                 log("Checking " + url.toString() + " on " +
                         mNetworkAgentInfo.networkInfo.getExtraInfo());
             }
             urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
-            urlConnection.setInstanceFollowRedirects(false);
+            urlConnection.setInstanceFollowRedirects(fetchPac);
             urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
             urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
             urlConnection.setUseCaches(false);
@@ -678,6 +720,11 @@
                 httpResponseCode = 204;
             }
 
+            if (httpResponseCode == 200 && fetchPac) {
+                if (DBG) log("PAC fetch 200 response interpreted as 204 response.");
+                httpResponseCode = 204;
+            }
+
             sendNetworkConditionsBroadcast(true /* response received */,
                     httpResponseCode != 204 /* isCaptivePortal */,
                     requestTimestamp, responseTimestamp);
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 59d5605..7f48768 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -62,11 +62,6 @@
     void process(ActiveSource newActive, int deviceType) {
         // Seq #17
         HdmiCecLocalDeviceTv tv = mSource;
-        ActiveSource activeSource = tv.getActiveSource();
-        if (activeSource.equals(newActive)) {
-            invokeCallback(HdmiControlManager.RESULT_SUCCESS);
-            return;
-        }
         HdmiDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress);
         if (device == null) {
             tv.startNewDeviceAction(newActive, deviceType);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ce52920..a8f6954 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -55,10 +55,6 @@
         mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                 mAddress, mService.getVendorId()));
         startQueuedActions();
-
-        // Switch TV input after bootup.
-        setActiveSource(true);
-        maySendActiveSource(Constants.ADDR_TV);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 43ef457..4f458e6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.HdmiRecordSources;
 import android.hardware.hdmi.HdmiTimerRecordSources;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -431,11 +432,15 @@
             return;
         }
         List<SendKeyAction> action = getActions(SendKeyAction.class);
+        int logicalAddress = findKeyReceiverAddress();
+        if (logicalAddress == mAddress) {
+            Slog.w(TAG, "Discard key event to itself :" + keyCode + " pressed:" + isPressed);
+            return;
+        }
         if (!action.isEmpty()) {
             action.get(0).processKeyEvent(keyCode, isPressed);
         } else {
             if (isPressed) {
-                int logicalAddress = findKeyReceiverAddress();
                 if (logicalAddress != Constants.ADDR_INVALID) {
                     addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
                     return;
@@ -580,6 +585,12 @@
         if (!isInDeviceList(address, path)) {
             handleNewDeviceAtTheTailOfActivePath(path);
         }
+
+        // Add the device ahead with default information to handle <Active Source>
+        // promptly, rather than waiting till the new device action is finished.
+        HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address));
+        addCecDevice(deviceInfo);
         startNewDeviceAction(ActiveSource.of(address, path), type);
         return true;
     }
@@ -878,11 +889,22 @@
         return oldStatus;
     }
 
+    @ServiceThreadOnly
+    private void updateArcFeatureStatus(int portId, boolean isConnected) {
+        assertRunOnServiceThread();
+        // HEAC 2.4, HEACT 5-15
+        // Should not activate ARC if +5V status is false.
+        HdmiPortInfo portInfo = mService.getPortInfo(portId);
+        if (portInfo.isArcSupported()) {
+            changeArcFeatureEnabled(isConnected);
+        }
+    }
+
     private void notifyArcStatusToAudioService(boolean enabled) {
         // Note that we don't set any name to ARC.
         mService.getAudioManager().setWiredDeviceConnectionState(
                 AudioSystem.DEVICE_OUT_HDMI_ARC,
-                enabled ? 1 : 0, "");
+                enabled ? 1 : 0, "", "");
     }
 
     /**
@@ -1456,6 +1478,25 @@
     }
 
     /**
+     * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
+     * the given routing path. This is the version accessible safely from threads
+     * other than service thread.
+     *
+     * @param path routing path or physical address
+     * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
+     */
+    HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
+        synchronized (mLock) {
+            for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+                if (info.getPhysicalAddress() == path) {
+                    return info;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
      * Whether a device of the specified physical address and logical address exists
      * in a device info list. However, both are minimal condition and it could
      * be different device from the original one.
@@ -1465,7 +1506,7 @@
      * @return true if exist; otherwise false
      */
     @ServiceThreadOnly
-    private boolean isInDeviceList(int logicalAddress, int physicalAddress) {
+    boolean isInDeviceList(int logicalAddress, int physicalAddress) {
         assertRunOnServiceThread();
         HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
         if (device == null) {
@@ -1490,6 +1531,7 @@
             // It covers seq #40, #43.
             hotplugActions.get(0).pollAllDevicesNow();
         }
+        updateArcFeatureStatus(portId, connected);
     }
 
     private void removeCecSwitches(int portId) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9f78c61..49a96d8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1196,7 +1196,7 @@
             }
             int activePath = tv.getActivePath();
             if (activePath != HdmiDeviceInfo.PATH_INVALID) {
-                HdmiDeviceInfo info = tv.getDeviceInfoByPath(activePath);
+                HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath);
                 return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId());
             }
             return null;
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 722be71..1bbd038 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -38,6 +38,7 @@
 
     private static final int POLLING_INTERVAL_MS = 5000;
     private static final int TIMEOUT_COUNT = 3;
+    private static final int AVR_COUNT_MAX = 3;
 
     // State in which waits for next polling
     private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
@@ -48,6 +49,12 @@
 
     private int mTimeoutCount = 0;
 
+    // Counter used to ensure the connection to AVR is stable. Occasional failure to get
+    // polling response from AVR despite its presence leads to unstable status flipping.
+    // This is a workaround to deal with it, by removing the device only if the removal
+    // is detected {@code AVR_COUNT_MAX} times in a row.
+    private int mAvrStatusCount = 0;
+
     /**
      * Constructor
      *
@@ -148,10 +155,22 @@
         BitSet removed = complement(currentInfos, polledResult);
         int index = -1;
         while ((index = removed.nextSetBit(index + 1)) != -1) {
+            if (index == Constants.ADDR_AUDIO_SYSTEM) {
+                ++mAvrStatusCount;
+                Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount);
+                if (mAvrStatusCount < AVR_COUNT_MAX) {
+                    continue;
+                }
+            }
             Slog.v(TAG, "Remove device by hot-plug detection:" + index);
             removeDevice(index);
         }
 
+        // Reset the counter if the ack is returned from AVR.
+        if (!removed.get(Constants.ADDR_AUDIO_SYSTEM)) {
+            mAvrStatusCount = 0;
+        }
+
         // Next, check added devices.
         BitSet added = complement(polledResult, currentInfos);
         index = -1;
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 3d64cc5..6753368 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -163,6 +163,12 @@
     }
 
     private void addDeviceInfo() {
+        // The device should be in the device list with default information.
+        if (!tv().isInDeviceList(mDeviceLogicalAddress, mDevicePhysicalAddress)) {
+            Slog.w(TAG, String.format("Device not found (%02x, %04x)",
+                    mDeviceLogicalAddress, mDevicePhysicalAddress));
+            return;
+        }
         if (mDisplayName == null) {
             mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress);
         }
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 2a1f7d6..e41b3da 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -25,7 +25,6 @@
 import com.android.internal.R;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
 
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -72,7 +71,6 @@
 import android.provider.Telephony.Carriers;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.SmsMessage;
-import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
@@ -91,7 +89,6 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Date;
-import java.util.List;
 import java.util.Map.Entry;
 import java.util.Properties;
 
@@ -395,7 +392,7 @@
 
     private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
         @Override
-        public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
+        public void addGpsStatusListener(IGpsStatusListener listener) {
             mListenerHelper.addListener(listener);
         }
 
@@ -681,7 +678,7 @@
         mListenerHelper = new GpsStatusListenerHelper(mHandler) {
             @Override
             protected boolean isAvailableInPlatform() {
-                return GpsLocationProvider.isSupported();
+                return isSupported();
             }
 
             @Override
@@ -1027,6 +1024,9 @@
             if (mC2KServerHost != null) {
                 native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
             }
+
+            mGpsMeasurementsProvider.onGpsEnabledChanged();
+            mGpsNavigationMessageProvider.onGpsEnabledChanged();
         } else {
             synchronized (mLock) {
                 mEnabled = false;
@@ -1060,6 +1060,9 @@
 
         // do this before releasing wakelock
         native_cleanup();
+
+        mGpsMeasurementsProvider.onGpsEnabledChanged();
+        mGpsNavigationMessageProvider.onGpsEnabledChanged();
     }
 
     @Override
@@ -1479,9 +1482,7 @@
         }
 
         if (wasNavigating != mNavigating) {
-            mListenerHelper.onGpsEnabledChanged(mNavigating);
-            mGpsMeasurementsProvider.onGpsEnabledChanged(mNavigating);
-            mGpsNavigationMessageProvider.onGpsEnabledChanged(mNavigating);
+            mListenerHelper.onStatusChanged(mNavigating);
 
             // send an intent to notify that the GPS has been enabled or disabled
             Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
diff --git a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
index 0514e0c..b327ca2 100644
--- a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
@@ -33,7 +33,7 @@
         extends RemoteListenerHelper<IGpsMeasurementsListener> {
     private static final String TAG = "GpsMeasurementsProvider";
 
-    public GpsMeasurementsProvider(Handler handler) {
+    protected GpsMeasurementsProvider(Handler handler) {
         super(handler, TAG);
     }
 
@@ -49,15 +49,19 @@
     }
 
     public void onCapabilitiesUpdated(boolean isGpsMeasurementsSupported) {
-        int status = isGpsMeasurementsSupported ?
-                GpsMeasurementsEvent.STATUS_READY :
-                GpsMeasurementsEvent.STATUS_NOT_SUPPORTED;
-        setSupported(isGpsMeasurementsSupported, new StatusChangedOperation(status));
+        setSupported(isGpsMeasurementsSupported);
+        updateResult();
+    }
+
+    public void onGpsEnabledChanged() {
+        if (tryUpdateRegistrationWithService()) {
+            updateResult();
+        }
     }
 
     @Override
     protected ListenerOperation<IGpsMeasurementsListener> getHandlerOperation(int result) {
-        final int status;
+        int status;
         switch (result) {
             case RESULT_SUCCESS:
                 status = GpsMeasurementsEvent.STATUS_READY;
@@ -70,6 +74,8 @@
             case RESULT_GPS_LOCATION_DISABLED:
                 status = GpsMeasurementsEvent.STATUS_GPS_LOCATION_DISABLED;
                 break;
+            case RESULT_UNKNOWN:
+                return null;
             default:
                 Log.v(TAG, "Unhandled addListener result: " + result);
                 return null;
@@ -77,15 +83,8 @@
         return new StatusChangedOperation(status);
     }
 
-    @Override
-    protected void handleGpsEnabledChanged(boolean enabled) {
-        int status = enabled ?
-                GpsMeasurementsEvent.STATUS_READY :
-                GpsMeasurementsEvent.STATUS_GPS_LOCATION_DISABLED;
-        foreach(new StatusChangedOperation(status));
-    }
-
-    private class StatusChangedOperation implements ListenerOperation<IGpsMeasurementsListener> {
+    private static class StatusChangedOperation
+            implements ListenerOperation<IGpsMeasurementsListener> {
         private final int mStatus;
 
         public StatusChangedOperation(int status) {
diff --git a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
index 13d22fc..e6bbe56 100644
--- a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
@@ -33,7 +33,7 @@
         extends RemoteListenerHelper<IGpsNavigationMessageListener> {
     private static final String TAG = "GpsNavigationMessageProvider";
 
-    public GpsNavigationMessageProvider(Handler handler) {
+    protected GpsNavigationMessageProvider(Handler handler) {
         super(handler, TAG);
     }
 
@@ -50,15 +50,19 @@
     }
 
     public void onCapabilitiesUpdated(boolean isGpsNavigationMessageSupported) {
-        int status = isGpsNavigationMessageSupported ?
-                GpsNavigationMessageEvent.STATUS_READY :
-                GpsNavigationMessageEvent.STATUS_NOT_SUPPORTED;
-        setSupported(isGpsNavigationMessageSupported, new StatusChangedOperation(status));
+        setSupported(isGpsNavigationMessageSupported);
+        updateResult();
+    }
+
+    public void onGpsEnabledChanged() {
+        if (tryUpdateRegistrationWithService()) {
+            updateResult();
+        }
     }
 
     @Override
     protected ListenerOperation<IGpsNavigationMessageListener> getHandlerOperation(int result) {
-        final int status;
+        int status;
         switch (result) {
             case RESULT_SUCCESS:
                 status = GpsNavigationMessageEvent.STATUS_READY;
@@ -71,6 +75,8 @@
             case RESULT_GPS_LOCATION_DISABLED:
                 status = GpsNavigationMessageEvent.STATUS_GPS_LOCATION_DISABLED;
                 break;
+            case RESULT_UNKNOWN:
+                return null;
             default:
                 Log.v(TAG, "Unhandled addListener result: " + result);
                 return null;
@@ -78,15 +84,7 @@
         return new StatusChangedOperation(status);
     }
 
-    @Override
-    protected void handleGpsEnabledChanged(boolean enabled) {
-        int status = enabled ?
-                GpsNavigationMessageEvent.STATUS_READY :
-                GpsNavigationMessageEvent.STATUS_GPS_LOCATION_DISABLED;
-        foreach(new StatusChangedOperation(status));
-    }
-
-    private class StatusChangedOperation
+    private static class StatusChangedOperation
             implements ListenerOperation<IGpsNavigationMessageListener> {
         private final int mStatus;
 
diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
index 376b4a5..53ff6c2 100644
--- a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
@@ -24,14 +24,9 @@
  * Implementation of a handler for {@link IGpsStatusListener}.
  */
 abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> {
-    public GpsStatusListenerHelper(Handler handler) {
+    protected GpsStatusListenerHelper(Handler handler) {
         super(handler, "GpsStatusListenerHelper");
-
-        Operation nullOperation = new Operation() {
-            @Override
-            public void execute(IGpsStatusListener iGpsStatusListener) throws RemoteException {}
-        };
-        setSupported(GpsLocationProvider.isSupported(), nullOperation);
+        setSupported(GpsLocationProvider.isSupported());
     }
 
     @Override
@@ -47,10 +42,9 @@
         return null;
     }
 
-    @Override
-    protected void handleGpsEnabledChanged(boolean enabled) {
+    public void onStatusChanged(boolean isNavigating) {
         Operation operation;
-        if (enabled) {
+        if (isNavigating) {
             operation = new Operation() {
                 @Override
                 public void execute(IGpsStatusListener listener) throws RemoteException {
@@ -114,5 +108,5 @@
         foreach(operation);
     }
 
-    private abstract class Operation implements ListenerOperation<IGpsStatusListener> { }
+    private interface Operation extends ListenerOperation<IGpsStatusListener> {}
 }
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 402b601..ec2828b 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -26,26 +26,31 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.Map;
 
 /**
  * A helper class, that handles operations in remote listeners, and tracks for remote process death.
  */
 abstract class RemoteListenerHelper<TListener extends IInterface> {
+
     protected static final int RESULT_SUCCESS = 0;
     protected static final int RESULT_NOT_AVAILABLE = 1;
     protected static final int RESULT_NOT_SUPPORTED = 2;
     protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
     protected static final int RESULT_INTERNAL_ERROR = 4;
+    protected static final int RESULT_UNKNOWN = 5;
 
     private final Handler mHandler;
     private final String mTag;
 
-    private final HashMap<IBinder, LinkedListener> mListenerMap = new HashMap<>();
+    private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
 
     private boolean mIsRegistered;
     private boolean mHasIsSupported;
     private boolean mIsSupported;
 
+    private int mLastReportedResult = RESULT_UNKNOWN;
+
     protected RemoteListenerHelper(Handler handler, String name) {
         Preconditions.checkNotNull(name);
         mHandler = handler;
@@ -110,33 +115,11 @@
         }
     }
 
-    public void onGpsEnabledChanged(boolean enabled) {
-        // handle first the sub-class implementation, so any error in registration can take
-        // precedence
-        handleGpsEnabledChanged(enabled);
-        synchronized (mListenerMap) {
-            if (!enabled) {
-                tryUnregister();
-                return;
-            }
-            if (mListenerMap.isEmpty()) {
-                return;
-            }
-            if (tryRegister()) {
-                // registration was successful, there is no need to update the state
-                return;
-            }
-            ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
-            foreachUnsafe(operation);
-        }
-    }
-
     protected abstract boolean isAvailableInPlatform();
     protected abstract boolean isGpsEnabled();
     protected abstract boolean registerWithService();
     protected abstract void unregisterFromService();
     protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
-    protected abstract void handleGpsEnabledChanged(boolean enabled);
 
     protected interface ListenerOperation<TListener extends IInterface> {
         void execute(TListener listener) throws RemoteException;
@@ -148,11 +131,40 @@
         }
     }
 
-    protected void setSupported(boolean value, ListenerOperation<TListener> notifier) {
+    protected void setSupported(boolean value) {
         synchronized (mListenerMap) {
             mHasIsSupported = true;
             mIsSupported = value;
-            foreachUnsafe(notifier);
+        }
+    }
+
+    protected boolean tryUpdateRegistrationWithService() {
+        synchronized (mListenerMap) {
+            if (!isGpsEnabled()) {
+                tryUnregister();
+                return true;
+            }
+            if (mListenerMap.isEmpty()) {
+                return true;
+            }
+            if (tryRegister()) {
+                // registration was successful, there is no need to update the state
+                return true;
+            }
+            ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
+            foreachUnsafe(operation);
+            return false;
+        }
+    }
+
+    protected void updateResult() {
+        synchronized (mListenerMap) {
+            int newResult = calculateCurrentResultUnsafe();
+            if (mLastReportedResult == newResult) {
+                return;
+            }
+            foreachUnsafe(getHandlerOperation(newResult));
+            mLastReportedResult = newResult;
         }
     }
 
@@ -183,6 +195,24 @@
         mIsRegistered = false;
     }
 
+    private int calculateCurrentResultUnsafe() {
+        // update statuses we already know about, starting from the ones that will never change
+        if (!isAvailableInPlatform()) {
+            return RESULT_NOT_AVAILABLE;
+        }
+        if (!mHasIsSupported || mListenerMap.isEmpty()) {
+            // we'll update once we have a supported status available
+            return RESULT_UNKNOWN;
+        }
+        if (!mIsSupported) {
+            return RESULT_NOT_SUPPORTED;
+        }
+        if (!isGpsEnabled()) {
+            return RESULT_GPS_LOCATION_DISABLED;
+        }
+        return RESULT_SUCCESS;
+    }
+
     private class LinkedListener implements IBinder.DeathRecipient {
         private final TListener mListener;
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index da63caa..df31158 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -250,77 +250,37 @@
         if (isPlaybackActive(false) || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
-        boolean isMute = direction == MediaSessionManager.DIRECTION_MUTE;
-        if (direction > 1) {
-            direction = 1;
-        } else if (direction < -1) {
-            direction = -1;
-        }
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
             if (mUseMasterVolume) {
                 // If this device only uses master volume and playback is local
                 // just adjust the master volume and return.
-                boolean isMasterMute = mAudioManager.isMasterMute();
-                if (isMute) {
-                    mAudioManagerInternal.setMasterMuteForUid(!isMasterMute,
-                            flags, packageName, mService.mICallback, uid);
-                } else {
-                    mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
-                            uid);
-                    if (isMasterMute) {
-                        mAudioManagerInternal.setMasterMuteForUid(false,
-                                flags, packageName, mService.mICallback, uid);
-                    }
-                }
+                mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
+                        uid);
                 return;
             }
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
-            boolean isStreamMute = mAudioManager.isStreamMute(stream);
             if (useSuggested) {
                 if (AudioSystem.isStreamActive(stream, 0)) {
-                    if (isMute) {
-                        mAudioManager.setStreamMute(stream, !isStreamMute);
-                    } else {
-                        mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
-                                flags, packageName, uid);
-                        if (isStreamMute && direction != 0) {
-                            mAudioManager.setStreamMute(stream, false);
-                        }
-                    }
+                    mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
+                            flags, packageName, uid);
                 } else {
                     flags |= previousFlagPlaySound;
-                    isStreamMute =
-                            mAudioManager.isStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE);
-                    if (isMute) {
-                        mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                                !isStreamMute);
-                    } else {
-                        mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
-                                AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
-                                uid);
-                        if (isStreamMute && direction != 0) {
-                            mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                                    false);
-                        }
-                    }
+                    mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+                            AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
+                            uid);
                 }
             } else {
-                if (isMute) {
-                    mAudioManager.setStreamMute(stream, !isStreamMute);
-                } else {
-                    mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
-                            packageName, uid);
-                    if (isStreamMute && direction != 0) {
-                        mAudioManager.setStreamMute(stream, false);
-                    }
-                }
+                mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+                        packageName, uid);
             }
         } else {
             if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                 // Nothing to do, the volume cannot be changed
                 return;
             }
-            if (isMute) {
+            if (direction == AudioManager.ADJUST_TOGGLE_MUTE
+                    || direction == AudioManager.ADJUST_MUTE
+                    || direction == AudioManager.ADJUST_UNMUTE) {
                 Log.w(TAG, "Muting remote playback is not supported");
                 return;
             }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 667d02a..4f6b8f2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -823,7 +823,7 @@
                 pw.println("User Records:");
                 count = mUserRecords.size();
                 for (int i = 0; i < count; i++) {
-                    UserRecord user = mUserRecords.get(i);
+                    UserRecord user = mUserRecords.get(mUserRecords.keyAt(i));
                     user.dumpLocked(pw, "");
                 }
             }
@@ -872,30 +872,10 @@
                 try {
                     String packageName = getContext().getOpPackageName();
                     if (mUseMasterVolume) {
-                        boolean isMasterMute = mAudioService.isMasterMute();
-                        if (direction == MediaSessionManager.DIRECTION_MUTE) {
-                            mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
-                        } else {
                             mAudioService.adjustMasterVolume(direction, flags, packageName);
-                            // Do not call setMasterMute when direction = 0 which is used just to
-                            // show the UI.
-                            if (isMasterMute && direction != 0) {
-                                mAudioService.setMasterMute(false, flags, packageName, mICallback);
-                            }
-                        }
                     } else {
-                        boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
-                        if (direction == MediaSessionManager.DIRECTION_MUTE) {
-                            mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
-                        } else {
-                            mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
-                                    flags, packageName);
-                            // Do not call setStreamMute when direction = 0 which is used just to
-                            // show the UI.
-                            if (isStreamMute && direction != 0) {
-                                mAudioManager.setStreamMute(suggestedStream, false);
-                            }
-                        }
+                        mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+                                flags, packageName);
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error adjusting default volume.", e);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 13b2b0f..359359e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3281,7 +3281,7 @@
                             // If the result set is different from when this
                             // was created, we need to clear it and re-ask the
                             // user their preference, if we're looking for an "always" type entry.
-                            if (always && !pa.mPref.sameSet(query, priority)) {
+                            if (always && !pa.mPref.sameSet(query)) {
                                 Slog.i(TAG, "Result set changed, dropping preferred activity for "
                                         + intent + " type " + resolvedType);
                                 if (DEBUG_PREFERRED) {
@@ -3381,7 +3381,7 @@
                 if (resolveInfo != null) {
                     List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
                     result.add(resolveInfo);
-                    return result;
+                    return filterIfNotPrimaryUser(result, userId);
                 }
                 // Check for cross profile results.
                 resolveInfo = queryCrossProfileIntents(
@@ -3394,17 +3394,38 @@
                     result.add(resolveInfo);
                     Collections.sort(result, mResolvePrioritySorter);
                 }
-                return result;
+                return filterIfNotPrimaryUser(result, userId);
             }
             final PackageParser.Package pkg = mPackages.get(pkgName);
             if (pkg != null) {
-                return mActivities.queryIntentForPackage(intent, resolvedType, flags,
-                        pkg.activities, userId);
+                return filterIfNotPrimaryUser(
+                        mActivities.queryIntentForPackage(
+                                intent, resolvedType, flags, pkg.activities, userId),
+                        userId);
             }
             return new ArrayList<ResolveInfo>();
         }
     }
 
+    /**
+     * Filter out activities with primaryUserOnly flag set, when current user is not the owner.
+     *
+     * @return filtered list
+     */
+    private List<ResolveInfo> filterIfNotPrimaryUser(List<ResolveInfo> resolveInfos, int userId) {
+        if (userId == UserHandle.USER_OWNER) {
+            return resolveInfos;
+        }
+        for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+            ResolveInfo info = resolveInfos.get(i);
+            if ((info.activityInfo.flags & ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {
+                resolveInfos.remove(i);
+            }
+        }
+        return resolveInfos;
+    }
+
+
     private ResolveInfo querySkipCurrentProfileIntents(
             List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
             int flags, int sourceUserId) {
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 69c1909..8e2e0cd 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -192,7 +192,7 @@
         }
     }
 
-    public boolean sameSet(List<ResolveInfo> query, int priority) {
+    public boolean sameSet(List<ResolveInfo> query) {
         if (mSetPackages == null) {
             return query == null;
         }
@@ -201,10 +201,10 @@
         }
         final int NQ = query.size();
         final int NS = mSetPackages.length;
+
         int numMatch = 0;
         for (int i=0; i<NQ; i++) {
             ResolveInfo ri = query.get(i);
-            if (ri.priority != priority) continue;
             ActivityInfo ai = ri.activityInfo;
             boolean good = false;
             for (int j=0; j<NS; j++) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 2af56fe..50b2262 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -677,7 +677,9 @@
         private AudioDevicePort mAudioSource;
         private List<AudioDevicePort> mAudioSink = new ArrayList<>();
         private AudioPatch mAudioPatch = null;
-        private float mCommittedVolume = 0.0f;
+        // Set to an invalid value for a volume, so that current volume can be applied at the
+        // first call to updateAudioConfigLocked().
+        private float mCommittedVolume = -1f;
         private float mSourceVolume = 0.0f;
 
         private TvStreamConfig mActiveConfig = null;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 08754f9..f8b40d1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -606,9 +606,8 @@
                 final int windowCount = windowList.size();
                 for (int i = 0; i < windowCount; i++) {
                     WindowState windowState = windowList.get(i);
-                    if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
-                            .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
-                            && !windowState.mWinAnimator.mEnterAnimationPending) {
+                    if (windowState.isOnScreen() &&
+                            !windowState.mWinAnimator.mEnterAnimationPending) {
                         outWindows.put(windowState.mLayer, windowState);
                     }
                 }
@@ -1237,7 +1236,6 @@
                     && windowType != WindowManager.LayoutParams.TYPE_DRAG
                     && windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
                     && windowType != WindowManager.LayoutParams.TYPE_POINTER
-                    && windowType != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
                     && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
                     && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
                     && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3b27cd2..a04f6cb 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -52,7 +52,7 @@
 
     final boolean voiceInteraction;
 
-    int groupId = -1;
+    Task mTask;
     boolean appFullscreen;
     int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     boolean layoutConfigChanges;
@@ -256,7 +256,8 @@
         mIsExiting = false;
         removeAllWindows();
 
-        final Task task = service.mTaskIdToTask.get(groupId);
+        // Use local variable because removeAppToken will null out mTask.
+        final Task task = mTask;
         if (task != null) {
             if (!task.removeAppToken(this)) {
                 Slog.e(WindowManagerService.TAG, "removeAppFromTaskLocked: token=" + this
@@ -295,8 +296,8 @@
         if (allAppWindows.size() > 0) {
             pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
         }
-        pw.print(prefix); pw.print("groupId="); pw.print(groupId);
-                pw.print(" appFullscreen="); pw.print(appFullscreen);
+        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("hiddenRequested="); pw.print(hiddenRequested);
                 pw.print(" clientHidden="); pw.print(clientHidden);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e3b53b0..8bbc5a9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -379,7 +379,7 @@
             ArrayList<Task> tasks = stack.getTasks();
             for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 final Task task = tasks.get(taskNdx);
-                pw.print("    mTaskId="); pw.println(task.taskId);
+                pw.print("    mTaskId="); pw.println(task.mTaskId);
                 AppTokenList tokens = task.mAppTokens;
                 for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) {
                     final AppWindowToken wtoken = tokens.get(tokenNdx);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 27ac32a..55dd911 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -239,9 +239,6 @@
         // As an optimization, we could try to prune the list of windows but this turns
         // out to be difficult because only the native code knows for sure which window
         // currently has touch focus.
-        final WindowStateAnimator universeBackground = mService.mAnimator.mUniverseBackground;
-        final int aboveUniverseLayer = mService.mAnimator.mAboveUniverseLayer;
-        boolean addedUniverse = false;
         boolean disableWallpaperTouchEvents = false;
 
         // If there's a drag in flight, provide a pseudowindow to catch drag input
@@ -299,20 +296,8 @@
                     mService.mDragState.sendDragStartedIfNeededLw(child);
                 }
 
-                if (universeBackground != null && !addedUniverse
-                        && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
-                    final WindowState u = universeBackground.mWin;
-                    if (u.mInputChannel != null && u.mInputWindowHandle != null) {
-                        addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
-                                u.mAttrs.type, true, u == mInputFocus, false);
-                    }
-                    addedUniverse = true;
-                }
-
-                if (child.mWinAnimator != universeBackground) {
-                    addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible,
-                            hasFocus, hasWallpaper);
-                }
+                addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
+                        hasWallpaper);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a4dfd8a4..d68c056 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -446,20 +446,6 @@
         mService.wallpaperCommandComplete(window, result);
     }
 
-    public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
-            float dsdx, float dtdx, float dsdy, float dtdy) {
-        synchronized(mService.mWindowMap) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                mService.setUniverseTransformLocked(
-                        mService.windowForClientLocked(this, window, true),
-                        alpha, offx, offy, dsdx, dtdx, dsdy, dtdy);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
         synchronized(mService.mWindowMap) {
             final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 80d727d..a9b26e2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -26,14 +26,13 @@
 class Task {
     TaskStack mStack;
     final AppTokenList mAppTokens = new AppTokenList();
-    final int taskId;
+    final int mTaskId;
     final int mUserId;
     boolean mDeferRemoval = false;
     final WindowManagerService mService;
 
-    Task(AppWindowToken wtoken, TaskStack stack, int userId, WindowManagerService service) {
-        taskId = wtoken.groupId;
-        mAppTokens.add(wtoken);
+    Task(int taskId, TaskStack stack, int userId, WindowManagerService service) {
+        mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
         mService = service;
@@ -45,38 +44,47 @@
 
     void addAppToken(int addPos, AppWindowToken wtoken) {
         final int lastPos = mAppTokens.size();
-        for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
-            if (mAppTokens.get(pos).removed) {
-                // addPos assumes removed tokens are actually gone.
-                ++addPos;
+        if (addPos >= lastPos) {
+            addPos = lastPos;
+        } else {
+            for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
+                if (mAppTokens.get(pos).removed) {
+                    // addPos assumes removed tokens are actually gone.
+                    ++addPos;
+                }
             }
         }
         mAppTokens.add(addPos, wtoken);
+        wtoken.mTask = this;
         mDeferRemoval = false;
     }
 
     void removeLocked() {
         if (!mAppTokens.isEmpty() && mStack.isAnimating()) {
-            if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + taskId);
+            if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
             mDeferRemoval = true;
             return;
         }
-        if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + taskId);
-        EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, taskId, "removeTask");
+        if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
+        EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask");
         mDeferRemoval = false;
         mStack.removeTask(this);
-        mService.mTaskIdToTask.delete(taskId);
+        mService.mTaskIdToTask.delete(mTaskId);
     }
 
     boolean removeAppToken(AppWindowToken wtoken) {
         boolean removed = mAppTokens.remove(wtoken);
         if (mAppTokens.size() == 0) {
-            EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_REMOVED, taskId,
+            EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_REMOVED, mTaskId,
                     "removeAppToken: last token");
             if (mDeferRemoval) {
                 removeLocked();
             }
         }
+        wtoken.mTask = null;
+        /* Leave mTaskId for now, it might be useful for debug
+        wtoken.mTaskId = -1;
+         */
         return removed;
     }
 
@@ -88,6 +96,6 @@
 
     @Override
     public String toString() {
-        return "{taskId=" + taskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
+        return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index d6741b3..1c9dfe0 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,11 +19,14 @@
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerService.TAG;
 
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Debug;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.TypedValue;
+
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -78,9 +81,26 @@
     /** Detach this stack from its display when animation completes. */
     boolean mDeferDetach;
 
+    // Contains configurations settings that are different from the global configuration due to
+    // stack specific operations. E.g. {@link #setBounds}.
+    Configuration mOverrideConfig;
+    // True if the stack was forced to fullscreen disregarding the override configuration.
+    private boolean mForceFullscreen;
+    // The {@link #mBounds} before the stack was forced to fullscreen. Will be restored as the
+    // stack bounds once the stack is no longer forced to fullscreen.
+    final private Rect mPreForceFullscreenBounds;
+
+    // When true this stack is at the top of the screen and should be layed out to extend under
+    // the status bar.
+    boolean mUnderStatusBar;
+
     TaskStack(WindowManagerService service, int stackId) {
         mService = service;
         mStackId = stackId;
+        mOverrideConfig = Configuration.EMPTY;
+        mForceFullscreen = false;
+        mPreForceFullscreenBounds = new Rect();
+        mUnderStatusBar = true;
         // TODO: remove bounds from log, they are always 0.
         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
                 mBounds.right, mBounds.bottom);
@@ -95,8 +115,6 @@
     }
 
     void resizeWindows() {
-        final boolean underStatusBar = mBounds.top == 0;
-
         final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
@@ -109,19 +127,29 @@
                                 "setBounds: Resizing " + win);
                         resizingWindows.add(win);
                     }
-                    win.mUnderStatusBar = underStatusBar;
                 }
             }
         }
     }
 
+    /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */
     boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFullscreen;
         if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
-            mFullscreen = mTmpRect.equals(bounds);
+            if (bounds == null) {
+                bounds = mTmpRect;
+                mFullscreen = true;
+            } else {
+                bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect
+                mFullscreen = mTmpRect.equals(bounds);
+            }
         }
 
+        if (bounds == null) {
+            // Can set to fullscreen if we don't have a display to get bounds from...
+            return false;
+        }
         if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
             return false;
         }
@@ -129,7 +157,8 @@
         mDimLayer.setBounds(bounds);
         mAnimationBackgroundSurface.setBounds(bounds);
         mBounds.set(bounds);
-
+        mUnderStatusBar = (mBounds.top == 0);
+        updateOverrideConfiguration();
         return true;
     }
 
@@ -137,6 +166,27 @@
         out.set(mBounds);
     }
 
+    private void updateOverrideConfiguration() {
+        final Configuration serviceConfig = mService.mCurConfiguration;
+        if (mFullscreen) {
+            mOverrideConfig = Configuration.EMPTY;
+            return;
+        }
+
+        if (mOverrideConfig == Configuration.EMPTY) {
+            mOverrideConfig  = new Configuration();
+        }
+
+        // TODO(multidisplay): Update Dp to that of display stack is on.
+        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        mOverrideConfig.screenWidthDp =
+                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
+        mOverrideConfig.screenHeightDp =
+                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
+        mOverrideConfig.smallestScreenWidthDp =
+                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
+    }
+
     void updateDisplayInfo() {
         if (mFullscreen && mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
@@ -148,6 +198,28 @@
         return mFullscreen;
     }
 
+    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
+     * Returns true if something happened.
+     */
+    boolean forceFullscreen(boolean forceFullscreen) {
+        if (mForceFullscreen == forceFullscreen) {
+            return false;
+        }
+        mForceFullscreen = forceFullscreen;
+        if (forceFullscreen) {
+            if (mFullscreen) {
+                return false;
+            }
+            mPreForceFullscreenBounds.set(mBounds);
+            return setBounds(null);
+        } else {
+            if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
+                return false;
+            }
+            return setBounds(mPreForceFullscreenBounds);
+        }
+    }
+
     boolean isAnimating() {
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
@@ -194,7 +266,7 @@
         if (toTop) {
             mDisplayContent.moveStack(this, true);
         }
-        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx);
+        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
     }
 
     void moveTaskToTop(Task task) {
@@ -224,10 +296,9 @@
             }
             mDisplayContent.layoutNeeded = true;
         }
-        final int taskId = task.taskId;
         for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
             final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
-            if (wtoken.groupId == taskId) {
+            if (wtoken.mTask == task) {
                 wtoken.mIsExiting = false;
                 mExitingAppTokens.remove(appNdx);
             }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 7e1f061..0c5d06a 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -79,9 +79,6 @@
      * seen. If multiple windows satisfy this, use the lowest window. */
     WindowState mWindowDetachedWallpaper = null;
 
-    WindowStateAnimator mUniverseBackground = null;
-    int mAboveUniverseLayer = 0;
-
     int mBulkUpdateParams = 0;
     Object mLastWindowFreezeSource;
 
@@ -794,6 +791,7 @@
             } else if (dumpAll) {
                 pw.print(subPrefix); pw.println("no ScreenRotationAnimation ");
             }
+            pw.println();
         }
 
         pw.println();
@@ -814,10 +812,6 @@
             pw.print(prefix); pw.print("mWindowDetachedWallpaper=");
                 pw.println(mWindowDetachedWallpaper);
         }
-        if (mUniverseBackground != null) {
-            pw.print(prefix); pw.print("mUniverseBackground="); pw.print(mUniverseBackground);
-                    pw.print(" mAboveUniverseLayer="); pw.println(mAboveUniverseLayer);
-        }
     }
 
     int getPendingLayoutChanges(final int displayId) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2c05e93..8214026 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -284,12 +284,6 @@
     // The name of the boot animation service in init.rc.
     private static final String BOOT_ANIMATION_SERVICE = "bootanim";
 
-    /** Minimum value for attachStack and resizeStack weight value */
-    public static final float STACK_WEIGHT_MIN = 0.2f;
-
-    /** Maximum value for attachStack and resizeStack weight value */
-    public static final float STACK_WEIGHT_MAX = 0.8f;
-
     static final int UPDATE_FOCUS_NORMAL = 0;
     static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
     static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
@@ -769,7 +763,7 @@
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
      */
-    boolean mInTouchMode = true;
+    boolean mInTouchMode;
 
     private ViewServer mViewServer;
     private final ArrayList<WindowChangeListener> mWindowChangeListeners =
@@ -813,9 +807,6 @@
                 WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
 
                 mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
-                mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
-                        * TYPE_LAYER_MULTIPLIER
-                        + TYPE_LAYER_OFFSET;
             }
         }, 0);
     }
@@ -830,6 +821,8 @@
                 com.android.internal.R.bool.config_sf_limitedAlpha);
         mHasPermanentDpad = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_hasPermanentDpad);
+        mInTouchMode = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_defaultInTouchMode);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplaySettings = new DisplaySettings();
@@ -2945,37 +2938,6 @@
         return null;
     }
 
-    public void setUniverseTransformLocked(WindowState window, float alpha,
-            float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy) {
-        Transformation transform = window.mWinAnimator.mUniverseTransform;
-        transform.setAlpha(alpha);
-        Matrix matrix = transform.getMatrix();
-        matrix.getValues(mTmpFloats);
-        mTmpFloats[Matrix.MTRANS_X] = offx;
-        mTmpFloats[Matrix.MTRANS_Y] = offy;
-        mTmpFloats[Matrix.MSCALE_X] = dsdx;
-        mTmpFloats[Matrix.MSKEW_Y] = dtdx;
-        mTmpFloats[Matrix.MSKEW_X] = dsdy;
-        mTmpFloats[Matrix.MSCALE_Y] = dtdy;
-        matrix.setValues(mTmpFloats);
-        final DisplayContent displayContent = window.getDisplayContent();
-        if (displayContent == null) {
-            return;
-        }
-
-        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        final RectF dispRect = new RectF(0, 0,
-                displayInfo.logicalWidth, displayInfo.logicalHeight);
-        matrix.mapRect(dispRect);
-        window.mGivenTouchableRegion.set(0, 0,
-                displayInfo.logicalWidth, displayInfo.logicalHeight);
-        window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top,
-                (int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE);
-        window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-        displayContent.layoutNeeded = true;
-        performLayoutAndPlaceSurfacesLocked();
-    }
-
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
         synchronized (mWindowMap) {
             if (mAccessibilityController != null) {
@@ -3612,15 +3574,15 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    private Task createTask(int taskId, int stackId, int userId, AppWindowToken atoken) {
-        if (DEBUG_STACK) Slog.i(TAG, "createTask: taskId=" + taskId + " stackId=" + stackId
+    private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken) {
+        if (DEBUG_STACK) Slog.i(TAG, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId
                 + " atoken=" + atoken);
         final TaskStack stack = mStackIdToStack.get(stackId);
         if (stack == null) {
             throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
         }
         EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
-        Task task = new Task(atoken, stack, userId, this);
+        Task task = new Task(taskId, stack, userId, this);
         mTaskIdToTask.put(taskId, task);
         stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */);
         return task;
@@ -3657,7 +3619,6 @@
             }
             atoken = new AppWindowToken(this, token, voiceInteraction);
             atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
-            atoken.groupId = taskId;
             atoken.appFullscreen = fullscreen;
             atoken.showWhenLocked = showWhenLocked;
             atoken.requestedOrientation = requestedOrientation;
@@ -3669,10 +3630,9 @@
 
             Task task = mTaskIdToTask.get(taskId);
             if (task == null) {
-                createTask(taskId, stackId, userId, atoken);
-            } else {
-                task.addAppToken(addPos, atoken);
+                task = createTaskLocked(taskId, stackId, userId, atoken);
             }
+            task.addAppToken(addPos, atoken);
 
             mTokenMap.put(token.asBinder(), atoken);
 
@@ -3685,28 +3645,27 @@
     }
 
     @Override
-    public void setAppGroupId(IBinder token, int groupId) {
+    public void setAppTask(IBinder token, int taskId) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
-                "setAppGroupId()")) {
+                "setAppTask()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
             final AppWindowToken atoken = findAppWindowToken(token);
             if (atoken == null) {
-                Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token);
+                Slog.w(TAG, "Attempted to set task id of non-existing app token: " + token);
                 return;
             }
-            final Task oldTask = mTaskIdToTask.get(atoken.groupId);
+            final Task oldTask = atoken.mTask;
             oldTask.removeAppToken(atoken);
 
-            atoken.groupId = groupId;
-            Task newTask = mTaskIdToTask.get(groupId);
+            Task newTask = mTaskIdToTask.get(taskId);
             if (newTask == null) {
-                newTask = createTask(groupId, oldTask.mStack.mStackId, oldTask.mUserId, atoken);
-            } else {
-                newTask.mAppTokens.add(atoken);
+                newTask =
+                        createTaskLocked(taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken);
             }
+            newTask.addAppToken(Integer.MAX_VALUE /* at top */, atoken);
         }
     }
 
@@ -3990,7 +3949,7 @@
     void setFocusedStackFrame() {
         final TaskStack stack;
         if (mFocusedApp != null) {
-            Task task = mTaskIdToTask.get(mFocusedApp.groupId);
+            final Task task = mFocusedApp.mTask;
             stack = task.mStack;
             final DisplayContent displayContent = task.getDisplayContent();
             if (displayContent != null) {
@@ -4812,7 +4771,7 @@
                         + " animating=" + wtoken.mAppAnimator.animating);
                 if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken: "
                         + wtoken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
-                final TaskStack stack = mTaskIdToTask.get(wtoken.groupId).mStack;
+                final TaskStack stack = wtoken.mTask.mStack;
                 if (delayed && !wtoken.allAppWindows.isEmpty()) {
                     // set the token aside because it has an active animation to be finished
                     if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -4877,7 +4836,7 @@
             final int numTasks = tasks.size();
             for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
                 final Task task = tasks.get(taskNdx);
-                Slog.v(TAG, "    Task #" + task.taskId + " activities from bottom to top:");
+                Slog.v(TAG, "    Task #" + task.mTaskId + " activities from bottom to top:");
                 AppTokenList tokens = task.mAppTokens;
                 final int numTokens = tokens.size();
                 for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
@@ -5128,7 +5087,12 @@
         }
     }
 
-    public void resizeStack(int stackId, Rect bounds) {
+    /**
+     * Re-sizes the specified stack and its containing windows.
+     * Returns a {@link Configuration} object that contains configurations settings
+     * that should be overridden due to the operation.
+     */
+    public Configuration resizeStack(int stackId, Rect bounds) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack == null) {
@@ -5140,6 +5104,7 @@
                 stack.getDisplayContent().layoutNeeded = true;
                 performLayoutAndPlaceSurfacesLocked();
             }
+            return new Configuration(stack.mOverrideConfig);
         }
     }
 
@@ -5152,6 +5117,26 @@
         bounds.setEmpty();
     }
 
+    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
+     * Returns a {@link Configuration} object that contains configurations settings
+     * that should be overridden due to the operation.
+     */
+    public Configuration forceStackToFullscreen(int stackId, boolean forceFullscreen) {
+        synchronized (mWindowMap) {
+            final TaskStack stack = mStackIdToStack.get(stackId);
+            if (stack == null) {
+                throw new IllegalArgumentException("resizeStack: stackId " + stackId
+                        + " not found.");
+            }
+            if (stack.forceFullscreen(forceFullscreen)) {
+                stack.resizeWindows();
+                stack.getDisplayContent().layoutNeeded = true;
+                performLayoutAndPlaceSurfacesLocked();
+            }
+            return new Configuration(stack.mOverrideConfig);
+        }
+    }
+
     // -------------------------------------------------------------
     // Misc IWindowSession methods
     // -------------------------------------------------------------
@@ -7068,7 +7053,7 @@
         return sw;
     }
 
-    boolean computeScreenConfigurationLocked(Configuration config) {
+    private boolean computeScreenConfigurationLocked(Configuration config) {
         if (!mDisplayReady) {
             return false;
         }
@@ -7951,16 +7936,16 @@
                     break;
 
                 case TAP_OUTSIDE_STACK: {
-                    int stackId;
-                    synchronized (mWindowMap) {
-                        stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
-                    }
-                    if (stackId >= 0) {
-                        try {
-                            mActivityManager.setFocusedStack(stackId);
-                        } catch (RemoteException e) {
-                        }
-                    }
+//                    int stackId;
+//                    synchronized (mWindowMap) {
+//                        stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
+//                    }
+//                    if (stackId >= 0) {
+//                        try {
+//                            mActivityManager.setFocusedStack(stackId);
+//                        } catch (RemoteException e) {
+//                        }
+//                    }
                 }
                 break;
                 case NOTIFY_ACTIVITY_DRAWN:
@@ -8476,7 +8461,7 @@
                 numRemoved++;
                 continue;
             } else if (lastBelow == i-1) {
-                if (w.mAttrs.type == TYPE_WALLPAPER || w.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) {
+                if (w.mAttrs.type == TYPE_WALLPAPER) {
                     lastBelow = i;
                 }
             }
@@ -8731,8 +8716,6 @@
                     + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
         }
 
-        WindowStateAnimator universeBackground = null;
-
         mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation);
         if (isDefaultDisplay) {
             // Not needed on non-default displays.
@@ -8790,8 +8773,7 @@
                     || ((win.isConfigChanged() || win.setInsetsChanged()) &&
                             ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
                             (win.mHasSurface && win.mAppToken != null &&
-                            win.mAppToken.layoutConfigChanges)))
-                    || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) {
+                            win.mAppToken.layoutConfigChanges)))) {
                 if (!win.mLayoutAttached) {
                     if (initial) {
                         //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
@@ -8815,16 +8797,6 @@
                     if (topAttached < 0) topAttached = i;
                 }
             }
-            if (win.mViewVisibility == View.VISIBLE
-                    && win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND
-                    && universeBackground == null) {
-                universeBackground = win.mWinAnimator;
-            }
-        }
-
-        if (mAnimator.mUniverseBackground  != universeBackground) {
-            mFocusMayChange = true;
-            mAnimator.mUniverseBackground = universeBackground;
         }
 
         boolean attachedBehindDream = false;
@@ -10367,11 +10339,6 @@
     }
 
     private WindowState computeFocusedWindowLocked() {
-        if (mAnimator.mUniverseBackground != null
-                && mAnimator.mUniverseBackground.mWin.canReceiveKeys()) {
-            return mAnimator.mUniverseBackground.mWin;
-        }
-
         final int displayCount = mDisplayContents.size();
         for (int i = 0; i < displayCount; i++) {
             final DisplayContent displayContent = mDisplayContents.valueAt(i);
@@ -10560,15 +10527,13 @@
                 scheduleAnimationLocked();
             } else {
                 screenRotationAnimation.kill();
-                screenRotationAnimation = null;
-                mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
+                mAnimator.setScreenRotationAnimationLocked(displayId, null);
                 updateRotation = true;
             }
         } else {
             if (screenRotationAnimation != null) {
                 screenRotationAnimation.kill();
-                screenRotationAnimation = null;
-                mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation);
+                mAnimator.setScreenRotationAnimationLocked(displayId, null);
             }
             updateRotation = true;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 978f5c3..bb95305 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -130,7 +130,8 @@
 
     int mLayoutSeq = -1;
 
-    Configuration mConfiguration = null;
+    private Configuration mConfiguration = Configuration.EMPTY;
+    private Configuration mOverrideConfig = Configuration.EMPTY;
     // Sticky answer to isConfigChanged(), remains true until new Configuration is assigned.
     // Used only on {@link #TYPE_KEYGUARD}.
     private boolean mConfigHasChanged;
@@ -341,10 +342,6 @@
     /** When true this window can be displayed on screens owther than mOwnerUid's */
     private boolean mShowToOwnerOnly;
 
-    /** When true this window is at the top of the screen and should be layed out to extend under
-     * the status bar */
-    boolean mUnderStatusBar = true;
-
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, final DisplayContent displayContent) {
@@ -508,8 +505,8 @@
 
         TaskStack stack = mAppToken != null ? getStack() : null;
         if (stack != null && !stack.isFullscreen()) {
-            getStackBounds(stack, mContainingFrame);
-            if (mUnderStatusBar) {
+            stack.getBounds(mContainingFrame);
+            if (stack.mUnderStatusBar) {
                 mContainingFrame.top = pf.top;
             }
         } else {
@@ -587,12 +584,13 @@
             y = mAttrs.y;
         }
 
+        // Make sure window fits in containing frame required by {@link Gravity#apply} call.
+        w = Math.min(w, pw);
+        h = Math.min(h, ph);
         Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
                 (int) (x + mAttrs.horizontalMargin * pw),
                 (int) (y + mAttrs.verticalMargin * ph), mFrame);
 
-        //System.out.println("Out: " + mFrame);
-
         // Now make sure the window fits in the overall display.
         Gravity.applyDisplay(mAttrs.gravity, df, mFrame);
 
@@ -792,14 +790,14 @@
     TaskStack getStack() {
         AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
         if (wtoken != null) {
-            Task task = mService.mTaskIdToTask.get(wtoken.groupId);
+            Task task = wtoken.mTask;
             if (task != null) {
                 if (task.mStack != null) {
                     return task.mStack;
                 }
                 Slog.e(TAG, "getStack: mStack null for task=" + task);
             } else {
-                Slog.e(TAG, "getStack: " + this + " couldn't find taskId=" + wtoken.groupId
+                Slog.e(TAG, "getStack: " + this + " couldn't find task for " + wtoken
                     + " Callers=" + Debug.getCallers(4));
             }
         }
@@ -807,10 +805,7 @@
     }
 
     void getStackBounds(Rect bounds) {
-        getStackBounds(getStack(), bounds);
-    }
-
-    private void getStackBounds(TaskStack stack, Rect bounds) {
+        final TaskStack stack = getStack();
         if (stack != null) {
             stack.getBounds(bounds);
             return;
@@ -1076,9 +1071,13 @@
     }
 
     boolean isConfigChanged() {
-        boolean configChanged = mConfiguration != mService.mCurConfiguration
-                && (mConfiguration == null
-                        || (mConfiguration.diff(mService.mCurConfiguration) != 0));
+        final TaskStack stack = getStack();
+        final Configuration overrideConfig =
+                (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY;
+        final Configuration serviceConfig = mService.mCurConfiguration;
+        boolean configChanged =
+                (mConfiguration != serviceConfig && mConfiguration.diff(serviceConfig) != 0)
+                || (mOverrideConfig != overrideConfig && !mOverrideConfig.equals(overrideConfig));
 
         if ((mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
             // Retain configuration changed status until resetConfiguration called.
@@ -1107,8 +1106,10 @@
         }
     }
 
-    void setConfiguration(final Configuration newConfig) {
+    private void setConfiguration(
+            final Configuration newConfig, final Configuration newOverrideConfig) {
         mConfiguration = newConfig;
+        mOverrideConfig = newOverrideConfig;
         mConfigHasChanged = false;
     }
 
@@ -1384,12 +1385,15 @@
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mCompatFrame);
             boolean configChanged = isConfigChanged();
+            final TaskStack stack = getStack();
+            final Configuration overrideConfig =
+                    (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY;
             if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
                 Slog.i(TAG, "Sending new config to window " + this + ": "
-                        + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH
-                        + " / " + mService.mCurConfiguration);
+                        + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH + " / config="
+                        + mService.mCurConfiguration + " overrideConfig=" + overrideConfig);
             }
-            setConfiguration(mService.mCurConfiguration);
+            setConfiguration(mService.mCurConfiguration, overrideConfig);
             if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING)
                 Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
 
@@ -1461,7 +1465,11 @@
     }
 
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
+                if (stack != null) {
+                    pw.print(" stackId="); pw.print(stack.mStackId);
+                }
                 pw.print(" mSession="); pw.print(mSession);
                 pw.print(" mClient="); pw.println(mClient.asBinder());
         pw.print(prefix); pw.print("mOwnerUid="); pw.print(mOwnerUid);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c2d8004..8202880 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -85,10 +85,6 @@
     final Context mContext;
     final boolean mIsWallpaper;
 
-    // If this is a universe background window, this is the transformation
-    // it is applying to the rest of the universe.
-    final Transformation mUniverseTransform = new Transformation();
-
     // Currently running animation.
     boolean mAnimating;
     boolean mLocalAnimating;
@@ -1096,9 +1092,6 @@
             if (appTransformation != null) {
                 tmpMatrix.postConcat(appTransformation.getMatrix());
             }
-            if (mAnimator.mUniverseBackground != null) {
-                tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
-            }
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
@@ -1164,9 +1157,6 @@
                         mHasClipRect = true;
                     }
                 }
-                if (mAnimator.mUniverseBackground != null) {
-                    mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
-                }
                 if (screenAnimation) {
                     mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
                 }
@@ -1192,15 +1182,12 @@
                 TAG, "computeShownFrameLocked: " + this +
                 " not attached, mAlpha=" + mAlpha);
 
-        final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null
-                && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
-                && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
         MagnificationSpec spec = null;
         //TODO (multidisplay): Magnification is supported only for the default display.
         if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
             spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
         }
-        if (applyUniverseTransformation || spec != null) {
+        if (spec != null) {
             final Rect frame = mWin.mFrame;
             final float tmpFloats[] = mService.mTmpFloats;
             final Matrix tmpMatrix = mWin.mTmpMatrix;
@@ -1208,10 +1195,6 @@
             tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
             tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
 
-            if (applyUniverseTransformation) {
-                tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
-            }
-
             if (spec != null && !spec.isNop()) {
                 tmpMatrix.postScale(spec.scale, spec.scale);
                 tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
@@ -1231,9 +1214,6 @@
             mWin.mShownFrame.set(x, y, x + w, y + h);
 
             mShownAlpha = mAlpha;
-            if (applyUniverseTransformation) {
-                mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
-            }
         } else {
             mWin.mShownFrame.set(mWin.mFrame);
             if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
@@ -1301,17 +1281,9 @@
                     displayInfo.logicalHeight - w.mCompatFrame.top);
         } else if (w.mLayer >= mService.mSystemDecorLayer) {
             // Above the decor layer is easy, just use the entire window.
-            // Unless we have a universe background...  in which case all the
-            // windows need to be cropped by the screen, so they don't cover
-            // the universe background.
-            if (mAnimator.mUniverseBackground == null) {
-                w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
-            } else {
-                applyDecorRect(mService.mScreenRect);
-            }
-        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
-                || w.mDecorFrame.isEmpty()) {
-            // The universe background isn't cropped, nor windows without policy decor.
+            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
+        } else if (w.mDecorFrame.isEmpty()) {
+            // Windows without policy decor aren't cropped.
             w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
         } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.mAnimating) {
             // If we're animating, the wallpaper crop should only be updated at the end of the
@@ -1932,11 +1904,6 @@
             pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
                     pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
         }
-        if (mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
-            pw.print(prefix); pw.print("mUniverseTransform=");
-                    mUniverseTransform.printShortString(pw);
-                    pw.println();
-        }
         if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
             pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
                     pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e4d0b77..b8f0d072 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1914,12 +1914,12 @@
         }
     }
 
-    public void setPasswordQuality(ComponentName who, int quality, int userHandle) {
+    public void setPasswordQuality(ComponentName who, int quality) {
         if (!mHasFeature) {
             return;
         }
+        final int userHandle = UserHandle.getCallingUserId();
         validateQualityConstant(quality);
-        enforceCrossUserPermission(userHandle);
 
         synchronized (this) {
             if (who == null) {
@@ -1963,11 +1963,11 @@
         }
     }
 
-    public void setPasswordMinimumLength(ComponentName who, int length, int userHandle) {
+    public void setPasswordMinimumLength(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2010,11 +2010,11 @@
         }
     }
 
-    public void setPasswordHistoryLength(ComponentName who, int length, int userHandle) {
+    public void setPasswordHistoryLength(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2057,11 +2057,11 @@
         }
     }
 
-    public void setPasswordExpirationTimeout(ComponentName who, long timeout, int userHandle) {
+    public void setPasswordExpirationTimeout(ComponentName who, long timeout) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2226,11 +2226,11 @@
         }
     }
 
-    public void setPasswordMinimumUpperCase(ComponentName who, int length, int userHandle) {
+    public void setPasswordMinimumUpperCase(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2273,8 +2273,8 @@
         }
     }
 
-    public void setPasswordMinimumLowerCase(ComponentName who, int length, int userHandle) {
-        enforceCrossUserPermission(userHandle);
+    public void setPasswordMinimumLowerCase(ComponentName who, int length) {
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2317,11 +2317,11 @@
         }
     }
 
-    public void setPasswordMinimumLetters(ComponentName who, int length, int userHandle) {
+    public void setPasswordMinimumLetters(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2364,11 +2364,11 @@
         }
     }
 
-    public void setPasswordMinimumNumeric(ComponentName who, int length, int userHandle) {
+    public void setPasswordMinimumNumeric(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2411,11 +2411,11 @@
         }
     }
 
-    public void setPasswordMinimumSymbols(ComponentName who, int length, int userHandle) {
+    public void setPasswordMinimumSymbols(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2458,11 +2458,11 @@
         }
     }
 
-    public void setPasswordMinimumNonLetter(ComponentName who, int length, int userHandle) {
+    public void setPasswordMinimumNonLetter(ComponentName who, int length) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2522,8 +2522,7 @@
 
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
-            getActiveAdminForCallerLocked(null,
-                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
             if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle)
                     || policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
                 return false;
@@ -2556,11 +2555,11 @@
         }
     }
 
-    public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, int userHandle) {
+    public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -2632,11 +2631,11 @@
         return strictestAdmin;
     }
 
-    public boolean resetPassword(String passwordOrNull, int flags, int userHandle) {
+    public boolean resetPassword(String passwordOrNull, int flags) {
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         enforceNotManagedProfile(userHandle, "reset the password");
 
         String password = passwordOrNull != null ? passwordOrNull : "";
@@ -2767,11 +2766,11 @@
         return true;
     }
 
-    public void setMaximumTimeToLock(ComponentName who, long timeMs, int userHandle) {
+    public void setMaximumTimeToLock(ComponentName who, long timeMs) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -3050,7 +3049,10 @@
             mHandler.post(new Runnable() {
                 public void run() {
                     try {
-                        ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
+                        IActivityManager am = ActivityManagerNative.getDefault();
+                        if (am.getCurrentUser().id == userHandle) {
+                            am.switchUser(UserHandle.USER_OWNER);
+                        }
                         if (!mUserManager.removeUser(userHandle)) {
                             Slog.w(LOG_TAG, "Couldn't remove user " + userHandle);
                         }
@@ -3231,11 +3233,10 @@
     }
 
     public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
-            String exclusionList, int userHandle) {
+            String exclusionList) {
         if (!mHasFeature) {
             return null;
         }
-        enforceCrossUserPermission(userHandle);
         synchronized(this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -3261,7 +3262,7 @@
             // If the user is not the owner, don't set the global proxy. Fail silently.
             if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
                 Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
-                        + userHandle + " is not permitted.");
+                        + UserHandle.getCallingUserId() + " is not permitted.");
                 return null;
             }
             if (proxySpec == null) {
@@ -3371,11 +3372,11 @@
      * Set the storage encryption request for a single admin.  Returns the new total request
      * status (for all admins).
      */
-    public int setStorageEncryption(ComponentName who, boolean encrypt, int userHandle) {
+    public int setStorageEncryption(ComponentName who, boolean encrypt) {
         if (!mHasFeature) {
             return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             // Check for permissions
             if (who == null) {
@@ -3507,11 +3508,11 @@
     /**
      * Set whether the screen capture is disabled for the user managed by the specified admin.
      */
-    public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+    public void setScreenCaptureDisabled(ComponentName who, boolean disabled) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -3566,11 +3567,11 @@
     /**
      * Set whether auto time is required by the specified admin (must be device owner).
      */
-    public void setAutoTimeRequired(ComponentName who, int userHandle, boolean required) {
+    public void setAutoTimeRequired(ComponentName who, boolean required) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -3617,11 +3618,11 @@
     /**
      * Disables all device cameras according to the specified admin.
      */
-    public void setCameraDisabled(ComponentName who, boolean disabled, int userHandle) {
+    public void setCameraDisabled(ComponentName who, boolean disabled) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -3666,11 +3667,11 @@
     /**
      * Selectively disable keyguard features.
      */
-    public void setKeyguardDisabledFeatures(ComponentName who, int which, int userHandle) {
+    public void setKeyguardDisabledFeatures(ComponentName who, int which) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         enforceNotManagedProfile(userHandle, "disable keyguard features");
         synchronized (this) {
             if (who == null) {
@@ -3920,7 +3921,7 @@
         Bundle userRestrictions = mUserManager.getUserRestrictions();
         mUserManager.setUserRestrictions(new Bundle(), userHandle);
         if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
-            audioManager.setMasterMute(false);
+            audioManager.adjustMasterVolume(AudioManager.ADJUST_UNMUTE, 0);
         }
         if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
             audioManager.setMicrophoneMute(false);
@@ -4216,11 +4217,11 @@
     }
 
     public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
-            PersistableBundle args, int userHandle) {
+            PersistableBundle args) {
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        final int userHandle = UserHandle.getCallingUserId();
         enforceNotManagedProfile(userHandle, "set trust agent configuration");
         synchronized (this) {
             if (admin == null) {
@@ -4841,7 +4842,8 @@
                     if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
                         iAudioService.setMicrophoneMute(true, who.getPackageName());
                     } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                        iAudioService.setMasterMute(true, 0, who.getPackageName(), null);
+                        iAudioService.adjustMasterVolume(AudioManager.ADJUST_MUTE, 0,
+                                who.getPackageName());
                     }
                 } catch (RemoteException re) {
                     Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
@@ -4906,7 +4908,8 @@
                     if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
                         iAudioService.setMicrophoneMute(false, who.getPackageName());
                     } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                        iAudioService.setMasterMute(false, 0, who.getPackageName(), null);
+                        iAudioService.adjustMasterVolume(AudioManager.ADJUST_UNMUTE, 0,
+                                who.getPackageName());
                     }
                 } catch (RemoteException re) {
                     Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
@@ -5361,8 +5364,6 @@
 
     @Override
     public void setMasterVolumeMuted(ComponentName who, boolean on) {
-        final ContentResolver contentResolver = mContext.getContentResolver();
-
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -5372,7 +5373,9 @@
             IAudioService iAudioService = IAudioService.Stub.asInterface(
                     ServiceManager.getService(Context.AUDIO_SERVICE));
             try{
-                iAudioService.setMasterMute(on, 0, who.getPackageName(), null);
+                iAudioService.adjustMasterVolume(
+                        on ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0,
+                        who.getPackageName());
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Failed to setMasterMute", re);
             }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 01a044e..9d13d3c 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -20,12 +20,16 @@
 import android.alsa.AlsaDevicesParser;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbInterface;
+import android.media.AudioService;
 import android.media.AudioSystem;
 import android.media.IAudioService;
+import android.midi.MidiDeviceInfo;
 import android.os.FileObserver;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -73,6 +77,9 @@
 
     private UsbAudioDevice mSelectedAudioDevice = null;
 
+    // UsbMidiDevice for USB peripheral mode (gadget) device
+    private UsbMidiDevice mPeripheralMidiDevice = null;
+
     private final class AlsaDevice {
         public static final int TYPE_UNKNOWN = 0;
         public static final int TYPE_PLAYBACK = 1;
@@ -173,15 +180,16 @@
                         " alsaDevice: " + alsaDevice);
             return;
         }
-        String params = ("card=" + alsaCard + ";device=" + alsaDevice);
 
+        String address = AudioService.makeAlsaAddressString(alsaCard, alsaDevice);
         try {
             // Playback Device
             if (audioDevice.mHasPlayback) {
                 int device = (audioDevice == mAccessoryAudioDevice ?
                         AudioSystem.DEVICE_OUT_USB_ACCESSORY :
                         AudioSystem.DEVICE_OUT_USB_DEVICE);
-                mAudioService.setWiredDeviceConnectionState(device, state, params);
+                mAudioService.setWiredDeviceConnectionState(
+                        device, state, address, audioDevice.mDeviceName);
             }
 
             // Capture Device
@@ -189,7 +197,8 @@
                int device = (audioDevice == mAccessoryAudioDevice ?
                         AudioSystem.DEVICE_IN_USB_ACCESSORY :
                         AudioSystem.DEVICE_IN_USB_DEVICE);
-                mAudioService.setWiredDeviceConnectionState(device, state, params);
+                mAudioService.setWiredDeviceConnectionState(
+                        device, state, address, audioDevice.mDeviceName);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
@@ -391,7 +400,17 @@
                 int device = mDevicesParser.getDefaultDeviceNum(addedCard);
                 AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
                 if (alsaDevice != null) {
-                    UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, usbDevice,
+                    Bundle properties = new Bundle();
+                    properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER,
+                            usbDevice.getManufacturerName());
+                    properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName());
+                    properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+                            usbDevice.getSerialNumber());
+                    properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, alsaDevice.mCard);
+                    properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice);
+                    properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
+
+                    UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
                             alsaDevice.mCard, alsaDevice.mDevice);
                     if (usbMidiDevice != null) {
                         mMidiDevices.put(usbDevice, usbMidiDevice);
@@ -410,17 +429,16 @@
         if (audioDevice != null) {
             if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) {
                 notifyDeviceState(audioDevice, false);
+                mSelectedAudioDevice = null;
+
+                // if there any external devices left, select one of them
+                selectDefaultDevice();
             }
         }
         UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice);
         if (usbMidiDevice != null) {
             IoUtils.closeQuietly(usbMidiDevice);
         }
-
-        mSelectedAudioDevice = null;
-
-        // if there any external devices left, select one of them
-        selectDefaultDevice();
     }
 
    /* package */ void setAccessoryAudioState(boolean enabled, int card, int device) {
@@ -437,6 +455,23 @@
         }
     }
 
+   /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
+        if (enabled) {
+            Bundle properties = new Bundle();
+            Resources r = mContext.getResources();
+            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString(
+                    com.android.internal.R.string.usb_midi_peripheral_manufacturer_name));
+            properties.putString(MidiDeviceInfo.PROPERTY_MODEL, r.getString(
+                    com.android.internal.R.string.usb_midi_peripheral_model_name));
+            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
+            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
+            mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device);
+        } else if (mPeripheralMidiDevice != null) {
+            IoUtils.closeQuietly(mPeripheralMidiDevice);
+            mPeripheralMidiDevice = null;
+        }
+   }
+
     //
     // Devices List
     //
diff --git a/services/usb/java/com/android/server/usb/UsbAudioDevice.java b/services/usb/java/com/android/server/usb/UsbAudioDevice.java
index 069d917..bdd28e4 100644
--- a/services/usb/java/com/android/server/usb/UsbAudioDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAudioDevice.java
@@ -53,7 +53,6 @@
         sb.append("UsbAudioDevice: [card: " + mCard);
         sb.append(", device: " + mDevice);
         sb.append(", name: " + mDeviceName);
-        sb.append(", description: " + mDeviceDescription);
         sb.append(", hasPlayback: " + mHasPlayback);
         sb.append(", hasCapture: " + mHasCapture);
         sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]");
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index 1cf00d2..e489279 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -31,6 +31,8 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.Base64;
 import com.android.server.FgThread;
@@ -206,6 +208,12 @@
                     break;
 
                 case MESSAGE_ADB_CONFIRM: {
+                    if ("trigger_restart_min_framework".equals(
+                            SystemProperties.get("vold.decrypt"))) {
+                        Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
+                        sendResponse("NO");
+                        break;
+                    }
                     String key = (String)msg.obj;
                     String fingerprints = getFingerprints(key);
                     if ("".equals(fingerprints)) {
@@ -279,7 +287,7 @@
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
             try {
-                mContext.startActivity(intent);
+                mContext.startActivityAsUser(intent, UserHandle.OWNER);
                 return true;
             } catch (ActivityNotFoundException e) {
                 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1426551..2fb6dbf 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -31,6 +31,7 @@
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbManager;
 import android.media.AudioManager;
+import android.midi.MidiDeviceInfo;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -85,6 +86,8 @@
             "/sys/class/android_usb/android0/f_rndis/ethaddr";
     private static final String AUDIO_SOURCE_PCM_PATH =
             "/sys/class/android_usb/android0/f_audio_source/pcm";
+    private static final String MIDI_ALSA_PATH =
+            "/sys/class/android_usb/android0/f_midi/alsa";
 
     private static final int MSG_UPDATE_STATE = 0;
     private static final int MSG_ENABLE_ADB = 1;
@@ -124,6 +127,7 @@
     private boolean mUseUsbNotification;
     private boolean mAdbEnabled;
     private boolean mAudioSourceEnabled;
+    private boolean mMidiEnabled;
     private Map<String, List<Pair<String, String>>> mOemModeMap;
     private String[] mAccessoryStrings;
     private UsbDebuggingManager mDebuggingManager;
@@ -618,6 +622,31 @@
             }
         }
 
+        private void updateMidiFunction() {
+            boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI);
+            if (enabled != mMidiEnabled) {
+                int card = -1;
+                int device = -1;
+
+                if (enabled) {
+                    Scanner scanner = null;
+                    try {
+                        scanner = new Scanner(new File(MIDI_ALSA_PATH));
+                        card = scanner.nextInt();
+                        device = scanner.nextInt();
+                    } catch (FileNotFoundException e) {
+                        Slog.e(TAG, "could not open MIDI PCM file", e);
+                    } finally {
+                        if (scanner != null) {
+                            scanner.close();
+                        }
+                    }
+                }
+                mUsbAlsaManager.setPeripheralMidiState(enabled, card, device);
+                mMidiEnabled = enabled;
+            }
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -636,6 +665,7 @@
                     if (mBootCompleted) {
                         updateUsbState();
                         updateAudioSourceFunction();
+                        updateMidiFunction();
                     }
                     break;
                 case MSG_ENABLE_ADB:
@@ -651,6 +681,7 @@
                     updateAdbNotification();
                     updateUsbState();
                     updateAudioSourceFunction();
+                    updateMidiFunction();
                     break;
                 case MSG_BOOT_COMPLETED:
                     mBootCompleted = true;
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 01b2df9..8d44905 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -17,13 +17,12 @@
 package com.android.server.usb;
 
 import android.content.Context;
-import android.hardware.usb.UsbDevice;
 import android.midi.MidiDeviceInfo;
 import android.midi.MidiDeviceServer;
 import android.midi.MidiManager;
+import android.midi.MidiPort;
 import android.midi.MidiReceiver;
 import android.midi.MidiSender;
-import android.midi.MidiUtils;
 import android.os.Bundle;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -45,10 +44,12 @@
 
     // for polling multiple FileDescriptors for MIDI events
     private final StructPollfd[] mPollFDs;
+    // streams for reading from ALSA driver
     private final FileInputStream[] mInputStreams;
+    // streams for writing to ALSA driver
     private final FileOutputStream[] mOutputStreams;
 
-    public static UsbMidiDevice create(Context context, UsbDevice usbDevice, int card, int device) {
+    public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
         MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
         if (midiManager == null) {
             Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
@@ -69,13 +70,6 @@
             return null;
         }
 
-        Bundle properties = new Bundle();
-        properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, usbDevice.getManufacturerName());
-        properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName());
-        properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, usbDevice.getSerialNumber());
-        properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
-        properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
-        properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
         MidiDeviceServer server = midiManager.createDeviceServer(subDevices, subDevices, properties,
                 false, MidiDeviceInfo.TYPE_USB);
         if (server == null) {
@@ -131,7 +125,7 @@
         new Thread() {
             @Override
             public void run() {
-                byte[] buffer = new byte[3];
+                byte[] buffer = new byte[MidiPort.MAX_PACKET_DATA_SIZE];
                 try {
                     boolean done = false;
                     while (!done) {
@@ -141,7 +135,8 @@
                             if ((pfd.revents & OsConstants.POLLIN) != 0) {
                                 // clear readable flag
                                 pfd.revents = 0;
-                                int count = readMessage(buffer, index);
+
+                                int count = mInputStreams[index].read(buffer);
                                 mOutputPortReceivers[index].onPost(buffer, 0, count,
                                         System.nanoTime());
                             } else if ((pfd.revents & (OsConstants.POLLERR
@@ -150,7 +145,7 @@
                             }
                         }
 
-                        // poll if none are readable
+                        // wait until we have a readable port
                         Os.poll(mPollFDs, -1 /* infinite timeout */);
                      }
                 } catch (IOException e) {
@@ -174,26 +169,6 @@
         }
     }
 
-    private int readMessage(byte[] buffer, int index) throws IOException {
-        FileInputStream inputStream = mInputStreams[index];
-
-        if (inputStream.read(buffer, 0, 1) != 1) {
-            Log.e(TAG, "could not read command byte");
-            return -1;
-        }
-        int dataSize = MidiUtils.getMessageDataSize(buffer[0]);
-        if (dataSize < 0) {
-            return -1;
-        }
-        if (dataSize > 0) {
-            if (inputStream.read(buffer, 1, dataSize) != dataSize) {
-                Log.e(TAG, "could not read command data");
-                return -1;
-            }
-        }
-        return dataSize + 1;
-    }
-
     private static native int nativeGetSubdeviceCount(int card, int device);
     private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index a180f44..108c0af 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -260,6 +260,7 @@
         /** @hide */
         public void onConferenceParticipantsChanged(Connection c,
                 List<ConferenceParticipant> participants) {}
+        public void onConferenceStarted() {}
     }
 
     /** @hide */
@@ -1001,14 +1002,11 @@
     /**
      * Informs listeners that this {@code Connection} has processed a character in the post-dial
      * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
-     * (b) it has encountered a "wait" character; and (c) it wishes to signal Telecom to play
-     * the corresponding DTMF tone locally.
+     * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
      *
      * @param nextChar The DTMF character that was just processed by the {@code Connection}.
-     *
-     * @hide
      */
-    public final void setNextPostDialWaitChar(char nextChar) {
+    public final void setNextPostDialChar(char nextChar) {
         checkImmutable();
         for (Listener l : mListeners) {
             l.onPostDialChar(this, nextChar);
@@ -1422,4 +1420,13 @@
             l.onConferenceParticipantsChanged(this, conferenceParticipants);
         }
     }
+
+    /**
+     * Notifies listeners that a conference call has been started.
+     */
+    protected void notifyConferenceStarted() {
+        for (Listener l : mListeners) {
+            l.onConferenceStarted();
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 052a481..a94c2f6 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -40,15 +40,13 @@
 /**
  * Represents a distinct method to place or receive a phone call. Apps which can place calls and
  * want those calls to be integrated into the dialer and in-call UI should build an instance of
- * this class and register it with the system using {@link TelecomManager#registerPhoneAccount}.
+ * this class and register it with the system using {@link TelecomManager}.
  * <p>
  * {@link TelecomManager} uses registered {@link PhoneAccount}s to present the user with
  * alternative options when placing a phone call. When building a {@link PhoneAccount}, the app
- * should supply a valid {@link PhoneAccountHandle} that references the {@link ConnectionService}
+ * should supply a valid {@link PhoneAccountHandle} that references the connection service
  * implementation Telecom will use to interact with the app.
- * @hide
  */
-@SystemApi
 public class PhoneAccount implements Parcelable {
 
     /**
@@ -62,7 +60,9 @@
      * if the user has explicitly selected it to be used as the default connection manager.
      * <p>
      * See {@link #getCapabilities}
+     * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_CONNECTION_MANAGER = 0x1;
 
     /**
@@ -76,6 +76,7 @@
      * <p>
      * {@hide}
      */
+    @SystemApi
     public static final int CAPABILITY_CALL_PROVIDER = 0x2;
 
     /**
@@ -94,6 +95,7 @@
      * See {@link #getCapabilities}
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_VIDEO_CALLING = 0x8;
 
     /**
@@ -111,6 +113,7 @@
      * See {@link #getCapabilities}
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_MULTI_USER = 0x20;
 
     /**
@@ -203,6 +206,7 @@
         }
 
         /** @hide */
+        @SystemApi
         public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
             mAccountHandle = accountHandle;
             return this;
@@ -333,6 +337,7 @@
          * @return The builder.
          * @hide
          */
+        @SystemApi
         public Builder addSupportedUriScheme(String uriScheme) {
             if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
                 this.mSupportedUriSchemes.add(uriScheme);
@@ -423,6 +428,7 @@
      * @return The builder.
      * @hide
      */
+    @SystemApi
     public Builder toBuilder() { return new Builder(this); }
 
     /**
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 97af41a..570f5fd 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -29,16 +29,13 @@
  * The unique identifier for a {@link PhoneAccount}. A {@code PhoneAccountHandle} is made of two
  * parts:
  * <ul>
- *  <li>The component name of the associated {@link ConnectionService}.</li>
+ *  <li>The component name of the associated connection service.</li>
  *  <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
  *      component name.</li>
  * </ul>
  *
- * See {@link PhoneAccount},
- * {@link TelecomManager#registerPhoneAccount TelecomManager.registerPhoneAccount}.
- * @hide
+ * See {@link PhoneAccount}, {@link TelecomManager}.
  */
-@SystemApi
 public class PhoneAccountHandle implements Parcelable {
     private final ComponentName mComponentName;
     private final String mId;
@@ -51,6 +48,7 @@
     }
 
     /** @hide */
+    @SystemApi
     public PhoneAccountHandle(
             ComponentName componentName,
             String id,
@@ -61,8 +59,8 @@
     }
 
     /**
-     * The {@code ComponentName} of the {@link android.telecom.ConnectionService} which is
-     * responsible for making phone calls using this {@code PhoneAccountHandle}.
+     * The {@code ComponentName} of the connection service which is responsible for making phone
+     * calls using this {@code PhoneAccountHandle}.
      *
      * @return A suitable {@code ComponentName}.
      */
@@ -72,9 +70,9 @@
 
     /**
      * A string that uniquely distinguishes this particular {@code PhoneAccountHandle} from all the
-     * others supported by the {@link ConnectionService} that created it.
+     * others supported by the connection service that created it.
      * <p>
-     * A {@code ConnectionService} must select identifiers that are stable for the lifetime of
+     * A connection service must select identifiers that are stable for the lifetime of
      * their users' relationship with their service, across many Android devices. For example, a
      * good set of identifiers might be the email addresses with which with users registered for
      * their accounts with a particular service. Depending on how a service chooses to operate,
@@ -92,6 +90,7 @@
      * @return the {@link UserHandle} to use when connecting to this PhoneAccount.
      * @hide
      */
+    @SystemApi
     public UserHandle getUserHandle() {
         return mUserHandle;
     }
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 073dcd5..a9b725b 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -60,11 +60,16 @@
                 mPendingConnections.remove(connection);
                 // Unconditionally initialize the connection ...
                 connection.setConnectionCapabilities(parcel.getConnectionCapabilities());
-                connection.setAddress(
-                        parcel.getHandle(), parcel.getHandlePresentation());
-                connection.setCallerDisplayName(
-                        parcel.getCallerDisplayName(),
-                        parcel.getCallerDisplayNamePresentation());
+                if (parcel.getHandle() != null
+                    || parcel.getState() != Connection.STATE_DISCONNECTED) {
+                    connection.setAddress(parcel.getHandle(), parcel.getHandlePresentation());
+                }
+                if (parcel.getCallerDisplayName() != null
+                    || parcel.getState() != Connection.STATE_DISCONNECTED) {
+                    connection.setCallerDisplayName(
+                            parcel.getCallerDisplayName(),
+                            parcel.getCallerDisplayNamePresentation());
+                }
                 // Set state after handle so that the client can identify the connection.
                 if (parcel.getState() == Connection.STATE_DISCONNECTED) {
                     connection.setDisconnected(parcel.getDisconnectCause());
@@ -235,8 +240,12 @@
 
         @Override
         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
+            RemoteConnection.VideoProvider remoteVideoProvider = null;
+            if (videoProvider != null) {
+                remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider);
+            }
             findConnectionForAction(callId, "setVideoProvider")
-                    .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider));
+                    .setVideoProvider(remoteVideoProvider);
         }
 
         @Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1a6b292..6c5f1c6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -106,7 +106,6 @@
      * {@link VideoProfile.VideoState#BIDIRECTIONAL},
      * {@link VideoProfile.VideoState#RX_ENABLED},
      * {@link VideoProfile.VideoState#TX_ENABLED}.
-     * @hide
      */
     public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
             "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
@@ -117,9 +116,7 @@
      * {@link PhoneAccountHandle} to use when making the call.
      * <p class="note">
      * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
             "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
 
@@ -139,10 +136,7 @@
      * {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
      * which contains metadata about the call. This {@link Bundle} will be saved into
      * {@code Call.Details}.
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_OUTGOING_CALL_EXTRAS =
             "android.telecom.extra.OUTGOING_CALL_EXTRAS";
 
@@ -554,9 +548,7 @@
      *
      * @param account The {@link PhoneAccountHandle}.
      * @return The {@link PhoneAccount} object.
-     * @hide
      */
-    @SystemApi
     public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
         try {
             if (isServiceConnected()) {
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index f5cb054..e62e994 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -21,8 +21,6 @@
 
 /**
  * Represents attributes of video calls.
- *
- * {@hide}
  */
 public class VideoProfile implements Parcelable {
     /**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index a59505c..69bb4bb 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -34,11 +34,9 @@
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
-import android.telephony.Rlog;
 import android.text.style.TtsSpan;
 import android.util.SparseIntArray;
 
-import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
@@ -1843,7 +1841,7 @@
         // to the list.
         number = extractNetworkPortionAlt(number);
 
-        Rlog.d(LOG_TAG, "subId:" + subId + ", number: " +  number + ", defaultCountryIso:" +
+        Rlog.d(LOG_TAG, "subId:" + subId + ", defaultCountryIso:" +
                 ((defaultCountryIso == null) ? "NULL" : defaultCountryIso));
 
         String emergencyNumbers = "";
@@ -2200,8 +2198,8 @@
         if (!TextUtils.isEmpty(dialStr)) {
             if (isReallyDialable(dialStr.charAt(0)) &&
                 isNonSeparator(dialStr)) {
-                String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
-                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+                String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
+                String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
                 if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
                             getFormatTypeFromCountryCode(currIso),
@@ -2223,7 +2221,7 @@
     public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
         if (!TextUtils.isEmpty(dialStr)) {
             if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
-                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+                String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
                 if (!TextUtils.isEmpty(defaultIso)) {
                     int format = getFormatTypeFromCountryCode(defaultIso);
                     return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
@@ -2339,15 +2337,13 @@
      *
      * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
      * @return A {@code CharSequence} with appropriate annotations.
-     *
-     * @hide
      */
-    public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
+    public static CharSequence getPhoneTtsSpannable(CharSequence phoneNumber) {
         if (phoneNumber == null) {
             return null;
         }
         Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
-        PhoneNumberUtils.ttsSpanAsPhoneNumber(spannable, 0, spannable.length());
+        PhoneNumberUtils.addPhoneTtsSpan(spannable, 0, spannable.length());
         return spannable;
     }
 
@@ -2358,19 +2354,83 @@
      * @param s A {@code Spannable} to annotate.
      * @param start The starting character position of the phone number in {@code s}.
      * @param end The ending character position of the phone number in {@code s}.
-     *
-     * @hide
      */
-    public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
-        s.setSpan(
-                new TtsSpan.TelephoneBuilder()
-                        .setNumberParts(splitAtNonNumerics(s.subSequence(start, end)))
-                        .build(),
+    public static void addPhoneTtsSpan(Spannable s, int start, int end) {
+        s.setSpan(getPhoneTtsSpan(s.subSequence(start, end).toString()),
                 start,
                 end,
                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
+    /**
+     * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
+     * containing a phone number in its entirety.
+     *
+     * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
+     * @return A {@code CharSequence} with appropriate annotations.
+     * @deprecated Renamed {@link #getPhoneTtsSpannable}.
+     *
+     * @hide
+     */
+    @Deprecated
+    public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
+        return getPhoneTtsSpannable(phoneNumber);
+    }
+
+    /**
+     * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
+     * annotating that location as containing a phone number.
+     *
+     * @param s A {@code Spannable} to annotate.
+     * @param start The starting character position of the phone number in {@code s}.
+     * @param end The ending character position of the phone number in {@code s}.
+     *
+     * @deprecated Renamed {@link #addPhoneTtsSpan}.
+     *
+     * @hide
+     */
+    @Deprecated
+    public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
+        addPhoneTtsSpan(s, start, end);
+    }
+
+    /**
+     * Create a {@code TtsSpan} for the supplied {@code String}.
+     *
+     * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
+     * @return A {@code TtsSpan} for {@param phoneNumberString}.
+     */
+    public static TtsSpan getPhoneTtsSpan(String phoneNumberString) {
+        if (phoneNumberString == null) {
+            return null;
+        }
+
+        // Parse the phone number
+        final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+        PhoneNumber phoneNumber = null;
+        try {
+            // Don't supply a defaultRegion so this fails for non-international numbers because
+            // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
+            // present
+            phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
+        } catch (NumberParseException ignored) {
+        }
+
+        // Build a telephone tts span
+        final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
+        if (phoneNumber == null) {
+            // Strip separators otherwise TalkBack will be silent
+            // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
+            builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
+        } else {
+            if (phoneNumber.hasCountryCode()) {
+                builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
+            }
+            builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
+        }
+        return builder.build();
+    }
+
     // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
     // a digit, to produce a result like "20 123 456".
     private static String splitAtNonNumerics(CharSequence number) {
@@ -2388,11 +2448,11 @@
 
     private static String getCurrentIdp(boolean useNanp) {
         String ps = null;
-        if(useNanp)
+        if (useNanp) {
             ps = NANP_IDP_STRING;
-        else{
+        } else {
             // in case, there is no IDD is found, we shouldn't convert it.
-            ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);                
+            ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
         }
         return ps;
     }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index adbe1d8..ca3c636 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -335,7 +335,7 @@
     public String toString() {
         return "{id=" + mId + ", iccId=" + mIccId + " simSlotIndex=" + mSimSlotIndex
                 + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
-                + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " number=" + mNumber
+                + " nameSource=" + mNameSource + " iconTint=" + mIconTint
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + "}";
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c67629d..aca94e9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1101,9 +1101,7 @@
             // What else can we do?
             return false;
         }
-        // FIXME: use better way to get roaming status instead of reading from system property
-        return Boolean.parseBoolean(TelephonyManager.getTelephonyProperty(phoneId,
-                TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, null));
+        return TelephonyManager.getDefault().isNetworkRoaming(subId);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ace945d..ba5a679 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -640,13 +640,9 @@
      */
     /** {@hide} */
     public String getDeviceId(int slotId) {
-        // FIXME methods taking slot id should not use subscription, instead us Uicc directly
-        int[] subId = SubscriptionManager.getSubId(slotId);
-        if (subId == null || subId.length == 0) {
-            return null;
-        }
+        // FIXME this assumes phoneId == slotId
         try {
-            return getSubscriberInfo().getDeviceIdForSubscriber(subId[0]);
+            return getSubscriberInfo().getDeviceIdForPhone(slotId);
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -703,7 +699,11 @@
     public String getNai(int slotId) {
         int[] subId = SubscriptionManager.getSubId(slotId);
         try {
-            return getSubscriberInfo().getNaiForSubscriber(subId[0]);
+            String nai = getSubscriberInfo().getNaiForSubscriber(subId[0]);
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Rlog.v(TAG, "Nai = " + nai);
+            }
+            return nai;
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -1077,7 +1077,7 @@
      * on a CDMA network).
      */
     public String getNetworkOperator() {
-        return getNetworkOperator(getDefaultSubscription());
+        return getNetworkOperatorForPhone(getDefaultPhone());
     }
 
     /**
@@ -1091,8 +1091,23 @@
      * @param subId
      */
     /** {@hide} */
-   public String getNetworkOperator(int subId) {
+   public String getNetworkOperatorForSubscription(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getNetworkOperatorForPhone(phoneId);
+     }
+
+    /**
+     * Returns the numeric name (MCC+MNC) of current registered operator
+     * for a particular subscription.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     *
+     * @param phoneId
+     * @hide
+     **/
+   public String getNetworkOperatorForPhone(int phoneId) {
         return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
      }
 
@@ -1130,7 +1145,7 @@
      * on a CDMA network).
      */
     public String getNetworkCountryIso() {
-        return getNetworkCountryIso(getDefaultSubscription());
+        return getNetworkCountryIsoForPhone(getDefaultPhone());
     }
 
     /**
@@ -1144,8 +1159,23 @@
      * @param subId for which Network CountryIso is returned
      */
     /** {@hide} */
-    public String getNetworkCountryIso(int subId) {
+    public String getNetworkCountryIsoForSubscription(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getNetworkCountryIsoForPhone(phoneId);
+    }
+
+    /**
+     * Returns the ISO country code equivalent of the current registered
+     * operator's MCC (Mobile Country Code) of a subscription.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     *
+     * @param phoneId for which Network CountryIso is returned
+     */
+    /** {@hide} */
+    public String getNetworkCountryIsoForPhone(int phoneId) {
         return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
     }
 
@@ -1537,6 +1567,34 @@
      * @see #getSimState
      */
     public String getSimOperator() {
+        return getSimOperatorNumeric();
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM. 5 or 6 decimal digits.
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     *
+     * @param subId for which SimOperator is returned
+     * @hide
+     */
+    public String getSimOperator(int subId) {
+        return getSimOperatorNumericForSubscription(subId);
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM. 5 or 6 decimal digits.
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     * @hide
+     */
+    public String getSimOperatorNumeric() {
         int subId = SubscriptionManager.getDefaultDataSubId();
         if (!SubscriptionManager.isUsableSubIdValue(subId)) {
             subId = SubscriptionManager.getDefaultSmsSubId();
@@ -1547,8 +1605,8 @@
                 }
             }
         }
-        Rlog.d(TAG, "getSimOperator(): default subId=" + subId);
-        return getSimOperator(subId);
+        Rlog.d(TAG, "getSimOperatorNumeric(): default subId=" + subId);
+        return getSimOperatorNumericForSubscription(subId);
     }
 
     /**
@@ -1560,14 +1618,24 @@
      * @see #getSimState
      *
      * @param subId for which SimOperator is returned
+     * @hide
      */
-    /** {@hide} */
-    public String getSimOperator(int subId) {
+    public String getSimOperatorNumericForSubscription(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
-        String operator = getTelephonyProperty(phoneId,
+        return getSimOperatorNumericForPhone(phoneId);
+    }
+
+   /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+     * <p>
+     *
+     * @param phoneId for which SimOperator is returned
+     * @hide
+     */
+    public String getSimOperatorNumericForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
-        Rlog.d(TAG, "getSimOperator: subId=" + subId + " operator=" + operator);
-        return operator;
     }
 
     /**
@@ -1578,7 +1646,7 @@
      * @see #getSimState
      */
     public String getSimOperatorName() {
-        return getSimOperatorName(getDefaultSubscription());
+        return getSimOperatorNameForPhone(getDefaultPhone());
     }
 
     /**
@@ -1589,30 +1657,61 @@
      * @see #getSimState
      *
      * @param subId for which SimOperatorName is returned
+     * @hide
      */
-    /** {@hide} */
-    public String getSimOperatorName(int subId) {
+    public String getSimOperatorNameForSubscription(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
-        return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
+        return getSimOperatorNameForPhone(phoneId);
+    }
+
+    /**
+     * Returns the Service Provider Name (SPN).
+     *
+     * @hide
+     */
+    public String getSimOperatorNameForPhone(int phoneId) {
+         return getTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
     }
 
     /**
      * Returns the ISO country code equivalent for the SIM provider's country code.
      */
     public String getSimCountryIso() {
-        return getSimCountryIso(getDefaultSubscription());
+        return getSimCountryIsoForPhone(getDefaultPhone());
     }
 
     /**
      * Returns the ISO country code equivalent for the SIM provider's country code.
      *
      * @param subId for which SimCountryIso is returned
+     *
+     * @hide
      */
-    /** {@hide} */
     public String getSimCountryIso(int subId) {
+        return getSimCountryIsoForSubscription(subId);
+    }
+
+    /**
+     * Returns the ISO country code equivalent for the SIM provider's country code.
+     *
+     * @param subId for which SimCountryIso is returned
+     *
+     * @hide
+     */
+    public String getSimCountryIsoForSubscription(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
-        return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
-                "");
+        return getSimCountryIsoForPhone(phoneId);
+    }
+
+    /**
+     * Returns the ISO country code equivalent for the SIM provider's country code.
+     *
+     * @hide
+     */
+    public String getSimCountryIsoForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
     }
 
     /**
@@ -3677,4 +3776,344 @@
            return false;
        }
    }
+
+   /**
+    * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+    *
+    * @hide
+    */
+    public void setSimOperatorNumeric(String numeric) {
+        int phoneId = getDefaultPhone();
+        setSimOperatorNumericForPhone(phoneId, numeric);
+    }
+
+   /**
+    * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+    *
+    * @hide
+    */
+    public void setSimOperatorNumericForPhone(int phoneId, String numeric) {
+        setTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, numeric);
+    }
+
+    /**
+     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+     *
+     * @hide
+     */
+    public void setSimOperatorName(String name) {
+        int phoneId = getDefaultPhone();
+        setSimOperatorNameForPhone(phoneId, name);
+    }
+
+    /**
+     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+     *
+     * @hide
+     */
+    public void setSimOperatorNameForPhone(int phoneId, String name) {
+        setTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, name);
+    }
+
+   /**
+    * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the default phone.
+    *
+    * @hide
+    */
+    public void setSimCountryIso(String iso) {
+        int phoneId = getDefaultPhone();
+        setSimCountryIsoForPhone(phoneId, iso);
+    }
+
+   /**
+    * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the given phone.
+    *
+    * @hide
+    */
+    public void setSimCountryIsoForPhone(int phoneId, String iso) {
+        setTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
+    }
+
+    /**
+     * Set TelephonyProperties.PROPERTY_SIM_STATE for the default phone.
+     *
+     * @hide
+     */
+    public void setSimState(String state) {
+        int phoneId = getDefaultPhone();
+        setSimStateForPhone(phoneId, state);
+    }
+
+    /**
+     * Set TelephonyProperties.PROPERTY_SIM_STATE for the given phone.
+     *
+     * @hide
+     */
+    public void setSimStateForPhone(int phoneId, String state) {
+        setTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_SIM_STATE, state);
+    }
+
+    /**
+     * Set baseband version for the default phone.
+     *
+     * @param version baseband version
+     * @hide
+     */
+    public void setBasebandVersion(String version) {
+        int phoneId = getDefaultPhone();
+        setBasebandVersionForPhone(phoneId, version);
+    }
+
+    /**
+     * Set baseband version by phone id.
+     *
+     * @param phoneId for which baseband version is set
+     * @param version baseband version
+     * @hide
+     */
+    public void setBasebandVersionForPhone(int phoneId, String version) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
+                    ((phoneId == 0) ? "" : Integer.toString(phoneId));
+            SystemProperties.set(prop, version);
+        }
+    }
+
+    /**
+     * Set phone type for the default phone.
+     *
+     * @param type phone type
+     *
+     * @hide
+     */
+    public void setPhoneType(int type) {
+        int phoneId = getDefaultPhone();
+        setPhoneType(phoneId, type);
+    }
+
+    /**
+     * Set phone type by phone id.
+     *
+     * @param phoneId for which phone type is set
+     * @param type phone type
+     *
+     * @hide
+     */
+    public void setPhoneType(int phoneId, int type) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            TelephonyManager.setTelephonyProperty(phoneId,
+                    TelephonyProperties.CURRENT_ACTIVE_PHONE, String.valueOf(type));
+        }
+    }
+
+    /**
+     * Get OTASP number schema for the default phone.
+     *
+     * @param defaultValue default value
+     * @return OTA SP number schema
+     *
+     * @hide
+     */
+    public String getOtaSpNumberSchema(String defaultValue) {
+        int phoneId = getDefaultPhone();
+        return getOtaSpNumberSchemaForPhone(phoneId, defaultValue);
+    }
+
+    /**
+     * Get OTASP number schema by phone id.
+     *
+     * @param phoneId for which OTA SP number schema is get
+     * @param defaultValue default value
+     * @return OTA SP number schema
+     *
+     * @hide
+     */
+    public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            return TelephonyManager.getTelephonyProperty(phoneId,
+                    TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA, defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Get SMS receive capable from system property for the default phone.
+     *
+     * @param defaultValue default value
+     * @return SMS receive capable
+     *
+     * @hide
+     */
+    public boolean getSmsReceiveCapable(boolean defaultValue) {
+        int phoneId = getDefaultPhone();
+        return getSmsReceiveCapableForPhone(phoneId, defaultValue);
+    }
+
+    /**
+     * Get SMS receive capable from system property by phone id.
+     *
+     * @param phoneId for which SMS receive capable is get
+     * @param defaultValue default value
+     * @return SMS receive capable
+     *
+     * @hide
+     */
+    public boolean getSmsReceiveCapableForPhone(int phoneId, boolean defaultValue) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            return Boolean.valueOf(TelephonyManager.getTelephonyProperty(phoneId,
+                    TelephonyProperties.PROPERTY_SMS_RECEIVE, String.valueOf(defaultValue)));
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Get SMS send capable from system property for the default phone.
+     *
+     * @param defaultValue default value
+     * @return SMS send capable
+     *
+     * @hide
+     */
+    public boolean getSmsSendCapable(boolean defaultValue) {
+        int phoneId = getDefaultPhone();
+        return getSmsSendCapableForPhone(phoneId, defaultValue);
+    }
+
+    /**
+     * Get SMS send capable from system property by phone id.
+     *
+     * @param phoneId for which SMS send capable is get
+     * @param defaultValue default value
+     * @return SMS send capable
+     *
+     * @hide
+     */
+    public boolean getSmsSendCapableForPhone(int phoneId, boolean defaultValue) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            return Boolean.valueOf(TelephonyManager.getTelephonyProperty(phoneId,
+                    TelephonyProperties.PROPERTY_SMS_SEND, String.valueOf(defaultValue)));
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Set the alphabetic name of current registered operator.
+     * @param name the alphabetic name of current registered operator.
+     * @hide
+     */
+    public void setNetworkOperatorName(String name) {
+        int phoneId = getDefaultPhone();
+        setNetworkOperatorNameForPhone(phoneId, name);
+    }
+
+    /**
+     * Set the alphabetic name of current registered operator.
+     * @param phoneId which phone you want to set
+     * @param name the alphabetic name of current registered operator.
+     * @hide
+     */
+    public void setNetworkOperatorNameForPhone(int phoneId, String name) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, name);
+        }
+    }
+
+    /**
+     * Set the numeric name (MCC+MNC) of current registered operator.
+     * @param operator the numeric name (MCC+MNC) of current registered operator
+     * @hide
+     */
+    public void setNetworkOperatorNumeric(String numeric) {
+        int phoneId = getDefaultPhone();
+        setNetworkOperatorNumericForPhone(phoneId, numeric);
+    }
+
+    /**
+     * Set the numeric name (MCC+MNC) of current registered operator.
+     * @param phoneId for which phone type is set
+     * @param operator the numeric name (MCC+MNC) of current registered operator
+     * @hide
+     */
+    public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) {
+        setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, numeric);
+    }
+
+    /**
+     * Set roaming state of the current network, for GSM purposes.
+     * @param isRoaming is network in romaing state or not
+     * @hide
+     */
+    public void setNetworkRoaming(boolean isRoaming) {
+        int phoneId = getDefaultPhone();
+        setNetworkRoamingForPhone(phoneId, isRoaming);
+    }
+
+    /**
+     * Set roaming state of the current network, for GSM purposes.
+     * @param phoneId which phone you want to set
+     * @param isRoaming is network in romaing state or not
+     * @hide
+     */
+    public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
+                    isRoaming ? "true" : "false");
+        }
+    }
+
+    /**
+     * Set the ISO country code equivalent of the current registered
+     * operator's MCC (Mobile Country Code).
+     * @param iso the ISO country code equivalent of the current registered
+     * @hide
+     */
+    public void setNetworkCountryIso(String iso) {
+        int phoneId = getDefaultPhone();
+        setNetworkCountryIsoForPhone(phoneId, iso);
+    }
+
+    /**
+     * Set the ISO country code equivalent of the current registered
+     * operator's MCC (Mobile Country Code).
+     * @param phoneId which phone you want to set
+     * @param iso the ISO country code equivalent of the current registered
+     * @hide
+     */
+    public void setNetworkCountryIsoForPhone(int phoneId, String iso) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            setTelephonyProperty(phoneId,
+                    TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
+        }
+    }
+
+    /**
+     * Set the network type currently in use on the device for data transmission.
+     * @param type the network type currently in use on the device for data transmission
+     * @hide
+     */
+    public void setDataNetworkType(int type) {
+        int phoneId = getDefaultPhone();
+        setDataNetworkTypeForPhone(phoneId, type);
+    }
+
+    /**
+     * Set the network type currently in use on the device for data transmission.
+     * @param phoneId which phone you want to set
+     * @param type the network type currently in use on the device for data transmission
+     * @hide
+     */
+    public void setDataNetworkTypeForPhone(int phoneId, int type) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            setTelephonyProperty(phoneId,
+                    TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
+                    ServiceState.rilRadioTechnologyToString(type));
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index eec5333..c91a59c 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -33,10 +33,10 @@
     String getNaiForSubscriber(int subId);
 
     /**
-     * Retrieves the unique device ID of a subId for the device, e.g., IMEI
+     * Retrieves the unique device ID of a phone for the device, e.g., IMEI
      * for GSM phones.
      */
-    String getDeviceIdForSubscriber(int subId);
+    String getDeviceIdForPhone(int phoneId);
 
     /**
      * Retrieves the IMEI.
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 6fdf121..70ac268 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -453,10 +453,6 @@
      */
     void setPremiumSmsPermission(String packageName, int permission);
 
-    /**
-     * Set the SMS send permission for the specified package.
-     * Requires system permission.
-     */
      /**
      * Set the SMS send permission for the specified package.
      * Requires system permission.
@@ -483,6 +479,14 @@
      */
     boolean isImsSmsSupportedForSubscriber(int subId);
 
+    /**
+     * User needs to pick SIM for SMS if multiple SIMs present and if current subId passed in is not
+     * active/valid.
+     * @param subId current subId for sending SMS
+     * @return true if SIM for SMS sending needs to be chosen
+     */
+    boolean isSmsSimPickActivityNeeded(int subId);
+
     /*
      * get user prefered SMS subId
      * @return subId id
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 62c92a1..e44969d 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -103,7 +103,7 @@
         }
         
         try {
-            mWm.setAppGroupId(null, 0);
+            mWm.setAppTask(null, 0);
             fail("IWindowManager.setAppGroupId did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index a2bd6d7..18036927 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -614,15 +614,27 @@
 
             int pos = value.indexOf('/');
             String idName = value.substring(pos + 1);
+            boolean create = value.startsWith("@+");
+            boolean isFrameworkId =
+                    mPlatformFile || value.startsWith("@android") || value.startsWith("@+android");
 
-            // if this is a framework id
-            if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
-                // look for idName in the android R classes
-                return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+            // Look for the idName in project or android R class depending on isPlatform.
+            if (create) {
+                Integer idValue;
+                if (isFrameworkId) {
+                    idValue = Bridge.getResourceId(ResourceType.ID, idName);
+                } else {
+                    idValue = mContext.getProjectCallback().getResourceId(ResourceType.ID, idName);
+                }
+                return idValue == null ? defValue : idValue;
             }
-
-            // look for idName in the project R class.
-            return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+            // This calls the same method as in if(create), but doesn't create a dynamic id, if
+            // one is not found.
+            if (isFrameworkId) {
+                return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+            } else {
+                return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+            }
         }
 
         // not a direct id valid reference? resolve it
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 5176419..d90271b 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -293,7 +293,7 @@
     }
 
     @Override
-    public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
+    public void setAppTask(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3953624..3441878 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -289,6 +289,11 @@
             value = mRenderResources.resolveResValue(value);
         }
 
+        if (value == null) {
+            // unable to find the attribute.
+            return false;
+        }
+
         // check if this is a style resource
         if (value instanceof StyleResourceValue) {
             // get the id that will represent this style.
@@ -296,7 +301,6 @@
             return true;
         }
 
-
         int a;
         // if this is a framework value.
         if (value.isFramework()) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 0f51d00..62a03e1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -191,12 +191,6 @@
     }
 
     @Override
-    public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
-            float dsdx, float dtdx, float dsdy, float dtdy) {
-        // pass for now.
-    }
-
-    @Override
     public IBinder asBinder() {
         // pass for now.
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
new file mode 100644
index 0000000..e5023b8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.bars;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Assumes that the AppCompat library is present in the project's classpath and creates an
+ * actionbar around it.
+ */
+public class AppCompatActionBar extends BridgeActionBar {
+
+    private Object mWindowDecorActionBar;
+    private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
+    private Class<?> mWindowActionBarClass;
+
+    /**
+     * Inflate the action bar and attach it to {@code parentView}
+     */
+    public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
+            @NonNull ViewGroup parentView) {
+        super(context, params, parentView);
+        int contentRootId = context.getProjectResourceValue(ResourceType.ID,
+                "action_bar_activity_content", 0);
+        View contentView = getDecorContent().findViewById(contentRootId);
+        if (contentView != null) {
+            assert contentView instanceof FrameLayout;
+            setContentRoot(((FrameLayout) contentView));
+        } else {
+            // Something went wrong. Create a new FrameLayout in the enclosing layout.
+            FrameLayout contentRoot = new FrameLayout(context);
+            setMatchParent(contentRoot);
+            mEnclosingLayout.addView(contentRoot);
+            setContentRoot(contentRoot);
+        }
+        try {
+            Class[] constructorParams = {View.class};
+            Object[] constructorArgs = {getDecorContent()};
+            mWindowDecorActionBar = params.getProjectCallback().loadView(WINDOW_ACTION_BAR_CLASS,
+                    constructorParams, constructorArgs);
+
+            mWindowActionBarClass = mWindowDecorActionBar == null ? null :
+                    mWindowDecorActionBar.getClass();
+            setupActionBar();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected ResourceValue getLayoutResource(BridgeContext context) {
+        // We always assume that the app has requested the action bar.
+        return context.getRenderResources().getProjectResource(ResourceType.LAYOUT,
+                "abc_screen_toolbar");
+    }
+
+    @Override
+    protected void setTitle(CharSequence title) {
+        if (title != null && mWindowDecorActionBar != null) {
+            Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class);
+            invoke(setTitle, mWindowDecorActionBar, title);
+        }
+    }
+
+    @Override
+    protected void setSubtitle(CharSequence subtitle) {
+        if (subtitle != null && mWindowDecorActionBar != null) {
+            Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class);
+            invoke(setSubtitle, mWindowDecorActionBar, subtitle);
+        }
+    }
+
+    @Override
+    protected void setIcon(String icon) {
+        // Do this only if the action bar doesn't already have an icon.
+        if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) {
+            if (((Boolean) invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar)
+            )) {
+                Drawable iconDrawable = getDrawable(icon, false);
+                if (iconDrawable != null) {
+                    Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class);
+                    invoke(setIcon, mWindowDecorActionBar, iconDrawable);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void setHomeAsUp(boolean homeAsUp) {
+        if (mWindowDecorActionBar != null) {
+            Method setHomeAsUp = getMethod(mWindowActionBarClass,
+                    "setDefaultDisplayHomeAsUpEnabled", boolean.class);
+            invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp);
+        }
+    }
+
+    @Override
+    public void createMenuPopup() {
+        // it's hard to addd menus to appcompat's actionbar, since it'll use a lot of reflection.
+        // so we skip it for now.
+    }
+
+    @Nullable
+    private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) {
+        try {
+            return owner == null ? null : owner.getMethod(name, parameterTypes);
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Nullable
+    private static Object invoke(Method method, Object owner, Object... args) {
+        try {
+            return method == null ? null : method.invoke(owner, args);
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper
+    @Nullable
+    private Drawable getDrawable(@NonNull String name, boolean isFramework) {
+        RenderResources res = mBridgeContext.getRenderResources();
+        ResourceValue value = res.findResValue(name, isFramework);
+        value = res.resolveResValue(value);
+        if (value != null) {
+            return ResourceHelper.getDrawable(value, mBridgeContext);
+        }
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
new file mode 100644
index 0000000..b29d25f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.bars;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+/**
+ * An abstraction over two implementations of the ActionBar - framework and appcompat.
+ */
+public abstract class BridgeActionBar {
+    // Store a reference to the context so that we don't have to cast it repeatedly.
+    @NonNull protected final BridgeContext mBridgeContext;
+    @NonNull protected final SessionParams mParams;
+    // A Layout that contains the inflated action bar. The menu popup is added to this layout.
+    @NonNull protected final ViewGroup mEnclosingLayout;
+
+    private final View mDecorContent;
+    private final ActionBarCallback mCallback;
+
+    @NonNull private FrameLayout mContentRoot;
+
+    public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
+            @NonNull ViewGroup parentView) {
+        mBridgeContext = context;
+        mParams = params;
+        mCallback = params.getProjectCallback().getActionBarCallback();
+        ResourceValue layoutName = getLayoutResource(context);
+        if (layoutName == null) {
+            throw new RuntimeException("Unable to find the layout for Action Bar.");
+        }
+        int layoutId;
+        if (layoutName.isFramework()) {
+            layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
+                    layoutName.getName(), 0);
+        } else {
+            layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
+                    layoutName.getName(), 0);
+
+        }
+        if (layoutId == 0) {
+            throw new RuntimeException(
+                    String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
+                            layoutName.getName(), layoutName.getResourceType()));
+        }
+        if (mCallback.isOverflowPopupNeeded()) {
+            // Create a RelativeLayout around the action bar, to which the overflow popup may be
+            // added.
+            mEnclosingLayout = new RelativeLayout(mBridgeContext);
+            setMatchParent(mEnclosingLayout);
+            parentView.addView(mEnclosingLayout);
+        } else {
+            mEnclosingLayout = parentView;
+        }
+
+        // Inflate action bar layout.
+        mDecorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
+
+    }
+
+    /**
+     * Returns the Layout Resource that should be used to inflate the action bar. This layout
+     * should cover the complete screen, and have a FrameLayout included, where the content will
+     * be inflated.
+     */
+    protected abstract ResourceValue getLayoutResource(BridgeContext context);
+
+    protected void setContentRoot(FrameLayout contentRoot) {
+        mContentRoot = contentRoot;
+    }
+
+    @NonNull
+    public FrameLayout getContentRoot() {
+        return mContentRoot;
+    }
+
+    /**
+     * Returns the view inflated. This should contain both the ActionBar and the app content in it.
+     */
+    protected View getDecorContent() {
+        return mDecorContent;
+    }
+
+    /** Setup things like the title, subtitle, icon etc. */
+    protected void setupActionBar() {
+        setTitle();
+        setSutTitle();
+        setIcon();
+        setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
+    }
+
+    protected abstract void setTitle(CharSequence title);
+    protected abstract void setSubtitle(CharSequence subtitle);
+    protected abstract void setIcon(String icon);
+    protected abstract void setHomeAsUp(boolean homeAsUp);
+
+    private void setTitle() {
+        RenderResources res = mBridgeContext.getRenderResources();
+
+        String title = mParams.getAppLabel();
+        ResourceValue titleValue = res.findResValue(title, false);
+        if (titleValue != null && titleValue.getValue() != null) {
+            setTitle(titleValue.getValue());
+        } else {
+            setTitle(title);
+        }
+    }
+
+    private void setSutTitle() {
+        String subTitle = mCallback.getSubTitle();
+        if (subTitle != null) {
+            setSubtitle(subTitle);
+        }
+    }
+
+    private void setIcon() {
+        String appIcon = mParams.getAppIcon();
+        if (appIcon != null) {
+            setIcon(appIcon);
+        }
+    }
+
+    public abstract void createMenuPopup();
+
+    public ActionBarCallback getCallBack() {
+        return mCallback;
+    }
+
+    protected static void setMatchParent(View view) {
+        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.MATCH_PARENT));
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
similarity index 76%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
index 2ff8d37..a1c9065 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
@@ -31,7 +31,7 @@
 import android.content.res.TypedArray;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
-import android.view.LayoutInflater;
+import android.view.InflateException;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
@@ -44,70 +44,30 @@
 
 import java.util.ArrayList;
 
-public class ActionBarLayout {
+/**
+ * Creates the ActionBar as done by the framework.
+ */
+public class FrameworkActionBar extends BridgeActionBar {
 
     private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
 
     // The Action Bar
-    @NonNull
-    private CustomActionBarWrapper mActionBar;
-
-    // Store another reference to the context so that we don't have to cast it repeatedly.
-    @NonNull
-    private final BridgeContext mBridgeContext;
-
-    @NonNull
-    private FrameLayout mContentRoot;
+    @NonNull private FrameworkActionBarWrapper mActionBar;
 
     // A fake parent for measuring views.
-    @Nullable
-    private ViewGroup mMeasureParent;
-
-    // A Layout that contains the inflated action bar. The menu popup is added to this layout.
-    @NonNull
-    private final RelativeLayout mEnclosingLayout;
+    @Nullable private ViewGroup mMeasureParent;
 
     /**
      * Inflate the action bar and attach it to {@code parentView}
      */
-    public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params,
+    public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
             @NonNull ViewGroup parentView) {
+        super(context, params, parentView);
 
-        mBridgeContext = context;
+        View decorContent = getDecorContent();
 
-        ResourceValue layoutName = context.getRenderResources()
-                .findItemInTheme(LAYOUT_ATTR_NAME, true);
-        if (layoutName != null) {
-            // We may need to resolve the reference obtained.
-            layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
-                    layoutName.isFramework());
-        }
-        int layoutId = 0;
-        String error = null;
-        if (layoutName == null) {
-            error = "Unable to find action bar layout (" + LAYOUT_ATTR_NAME
-                    + ") in the current theme.";
-        } else {
-            layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
-                    layoutName.getName(), 0);
-            if (layoutId == 0) {
-                error = String.format("Unable to resolve attribute \"%s\" of type \"%s\"",
-                        layoutName.getName(), layoutName.getResourceType());
-            }
-        }
-        if (layoutId == 0) {
-            throw new RuntimeException(error);
-        }
-        // Create a RelativeLayout to hold the action bar. The layout is needed so that we may
-        // add the menu popup to it.
-        mEnclosingLayout = new RelativeLayout(mBridgeContext);
-        setMatchParent(mEnclosingLayout);
-        parentView.addView(mEnclosingLayout);
-
-        // Inflate action bar layout.
-        View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
-
-        mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
+        mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
+                decorContent);
 
         FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
 
@@ -117,27 +77,62 @@
             contentRoot = new FrameLayout(context);
             setMatchParent(contentRoot);
             mEnclosingLayout.addView(contentRoot);
-            mContentRoot = contentRoot;
+            setContentRoot(contentRoot);
         } else {
-            mContentRoot = contentRoot;
-            mActionBar.setupActionBar();
+            setContentRoot(contentRoot);
+            setupActionBar();
             mActionBar.inflateMenus();
         }
     }
 
-    private void setMatchParent(View view) {
-        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT));
+    @Override
+    protected ResourceValue getLayoutResource(BridgeContext context) {
+        ResourceValue layoutName =
+                context.getRenderResources().findItemInTheme(LAYOUT_ATTR_NAME, true);
+        if (layoutName != null) {
+            // We may need to resolve the reference obtained.
+            layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
+                    layoutName.isFramework());
+        }
+        if (layoutName == null) {
+             throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME
+                    + ") in the current theme.");
+        }
+        return layoutName;
+    }
+
+    @Override
+    protected void setupActionBar() {
+        super.setupActionBar();
+        mActionBar.setupActionBar();
+    }
+
+    @Override
+    protected void setHomeAsUp(boolean homeAsUp) {
+        mActionBar.setHomeAsUp(homeAsUp);
+    }
+
+    @Override
+    protected void setTitle(CharSequence title) {
+        mActionBar.setTitle(title);
+    }
+
+    @Override
+    protected void setSubtitle(CharSequence subtitle) {
+        mActionBar.setSubTitle(subtitle);
+    }
+
+    @Override
+    protected void setIcon(String icon) {
+        mActionBar.setIcon(icon);
     }
 
     /**
      * Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
      * the content frame which shall serve as the new content root.
      */
+    @Override
     public void createMenuPopup() {
-        assert mEnclosingLayout.getChildCount() == 1
-                : "Action Bar Menus have already been created.";
-
         if (!isOverflowPopupNeeded()) {
             return;
         }
@@ -193,11 +188,6 @@
         return needed;
     }
 
-    @NonNull
-    public FrameLayout getContentRoot() {
-        return mContentRoot;
-    }
-
     // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
     private int measureContentWidth(@NonNull ListAdapter adapter) {
         // Menus don't tend to be long, so this is more sane than it looks.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
similarity index 81%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
index 6db722e..44c2cd8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
@@ -19,10 +19,8 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.ActionBarCallback;
-import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.SessionParams;
 import com.android.internal.R;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
@@ -54,10 +52,9 @@
 /**
  * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
  */
-public abstract class CustomActionBarWrapper {
+public abstract class FrameworkActionBarWrapper {
 
     @NonNull protected ActionBar mActionBar;
-    @NonNull protected SessionParams mParams;
     @NonNull protected ActionBarCallback mCallback;
     @NonNull protected BridgeContext mContext;
 
@@ -68,49 +65,48 @@
      *                     ?attr/windowActionBarFullscreenDecorLayout
      */
     @NonNull
-    public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
-            @NonNull SessionParams params, @NonNull View decorContent) {
+    public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
+            @NonNull ActionBarCallback callback, @NonNull View decorContent) {
         View view = decorContent.findViewById(R.id.action_bar);
         if (view instanceof Toolbar) {
-            return new ToolbarWrapper(context, params, ((Toolbar) view));
+            return new ToolbarWrapper(context, callback, (Toolbar) view);
         } else if (view instanceof ActionBarView) {
-            return new WindowActionBarWrapper(context, params, decorContent,
-                    ((ActionBarView) view));
+            return new WindowActionBarWrapper(context, callback, decorContent,
+                    (ActionBarView) view);
         } else {
             throw new IllegalStateException("Can't make an action bar out of " +
                     view.getClass().getSimpleName());
         }
     }
 
-    CustomActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
+    FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
             @NonNull ActionBar actionBar) {
         mActionBar = actionBar;
-        mParams = params;
-        mCallback = params.getProjectCallback().getActionBarCallback();
+        mCallback = callback;
         mContext = context;
     }
 
+    /** A call to setup any custom properties. */
     protected void setupActionBar() {
-        // Do the things that are common to all implementations.
-        RenderResources res = mContext.getRenderResources();
+        // Nothing to do here.
+    }
 
-        String title = mParams.getAppLabel();
-        ResourceValue titleValue = res.findResValue(title, false);
-        if (titleValue != null && titleValue.getValue() != null) {
-            mActionBar.setTitle(titleValue.getValue());
-        } else {
-            mActionBar.setTitle(title);
-        }
+    public void setTitle(CharSequence title) {
+        mActionBar.setTitle(title);
+    }
 
-        String subTitle = mCallback.getSubTitle();
+    public void setSubTitle(CharSequence subTitle) {
         if (subTitle != null) {
             mActionBar.setSubtitle(subTitle);
         }
+    }
 
-        // Add show home as up icon.
-        if (mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP) {
-            mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP);
-        }
+    public void setHomeAsUp(boolean homeAsUp) {
+        mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
+    }
+
+    public void setIcon(String icon) {
+        // Nothing to do.
     }
 
     protected boolean isSplit() {
@@ -186,15 +182,14 @@
      * Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
      * Toolbar using a common API.
      */
-    private static class ToolbarWrapper extends CustomActionBarWrapper {
+    private static class ToolbarWrapper extends FrameworkActionBarWrapper {
 
         @NonNull
         private final Toolbar mToolbar;  // This is the view.
 
-        ToolbarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
+        ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
                 @NonNull Toolbar toolbar) {
-            super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback())
-            );
+            super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback()));
             mToolbar = toolbar;
         }
 
@@ -248,19 +243,17 @@
      * Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
      * access to it using a common API.
      */
-    private static class WindowActionBarWrapper extends CustomActionBarWrapper {
+    private static class WindowActionBarWrapper extends FrameworkActionBarWrapper {
 
-        @NonNull
-        private final WindowDecorActionBar mActionBar;
-        @NonNull
-        private final ActionBarView mActionBarView;
-        @NonNull
-        private final View mDecorContentRoot;
+        @NonNull private final WindowDecorActionBar mActionBar;
+        @NonNull private final ActionBarView mActionBarView;
+        @NonNull private final View mDecorContentRoot;
         private MenuBuilder mMenuBuilder;
 
-        public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
-                @NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) {
-            super(context, params, new WindowDecorActionBar(decorContentRoot));
+        public WindowActionBarWrapper(@NonNull BridgeContext context,
+                @NonNull ActionBarCallback callback, @NonNull View decorContentRoot,
+                @NonNull ActionBarView actionBarView) {
+            super(context, callback, new WindowDecorActionBar(decorContentRoot));
             mActionBarView = actionBarView;
             mActionBar = ((WindowDecorActionBar) super.mActionBar);
             mDecorContentRoot = decorContentRoot;
@@ -268,7 +261,6 @@
 
         @Override
         protected void setupActionBar() {
-            super.setupActionBar();
 
             // Set the navigation mode.
             int navMode = mCallback.getNavigationMode();
@@ -278,16 +270,6 @@
                 setupTabs(3);
             }
 
-            String icon = mParams.getAppIcon();
-            // If the action bar style doesn't specify an icon, set the icon obtained from the
-            // session params.
-            if (!mActionBar.hasIcon() && icon != null) {
-                Drawable iconDrawable = getDrawable(icon, false);
-                if (iconDrawable != null) {
-                    mActionBar.setIcon(iconDrawable);
-                }
-            }
-
             // Set action bar to be split, if needed.
             ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
             if (splitView != null) {
@@ -300,6 +282,17 @@
         }
 
         @Override
+        public void setIcon(String icon) {
+            // Set the icon only if the action bar doesn't specify an icon.
+            if (!mActionBar.hasIcon() && icon != null) {
+                Drawable iconDrawable = getDrawable(icon, false);
+                if (iconDrawable != null) {
+                    mActionBar.setIcon(iconDrawable);
+                }
+            }
+        }
+
+        @Override
         protected void inflateMenus() {
             super.inflateMenus();
             // The super implementation doesn't set the menu on the view. Set it here.
@@ -340,7 +333,7 @@
 
         @Override
         int getMenuPopupMargin() {
-            return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics());
+            return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics());
         }
 
         // TODO: Use an adapter, like List View to set up tabs.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index fd976af..84ee914 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -51,11 +51,13 @@
 import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.android.SessionParamsFlags;
+import com.android.layoutlib.bridge.bars.BridgeActionBar;
+import com.android.layoutlib.bridge.bars.AppCompatActionBar;
 import com.android.layoutlib.bridge.bars.Config;
 import com.android.layoutlib.bridge.bars.NavigationBar;
 import com.android.layoutlib.bridge.bars.StatusBar;
 import com.android.layoutlib.bridge.bars.TitleBar;
-import com.android.layoutlib.bridge.bars.ActionBarLayout;
+import com.android.layoutlib.bridge.bars.FrameworkActionBar;
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
 import com.android.resources.Density;
@@ -354,7 +356,7 @@
 
                 // if the theme says no title/action bar, then the size will be 0
                 if (mActionBarSize > 0) {
-                    ActionBarLayout actionBar = createActionBar(context, params, backgroundLayout);
+                    BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
                     actionBar.createMenuPopup();
                     mContentRoot = actionBar.getContentRoot();
                 } else if (mTitleBarSize > 0) {
@@ -1190,8 +1192,22 @@
         // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
         if (mIsThemeAppCompat == null) {
             StyleResourceValue defaultTheme = resources.getDefaultTheme();
-            StyleResourceValue val = resources.getStyle("Theme.AppCompat", false);
-            mIsThemeAppCompat = defaultTheme == val || resources.themeIsParentOf(val, defaultTheme);
+          // We can't simply check for parent using resources.themeIsParentOf() since the
+          // inheritance structure isn't really what one would expect. The first common parent
+          // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
+            boolean isThemeAppCompat = false;
+            for (int i = 0; i < 50; i++) {
+                // for loop ensures that we don't run into cyclic theme inheritance.
+                if (defaultTheme.getName().startsWith("Theme.AppCompat")) {
+                    isThemeAppCompat = true;
+                    break;
+                }
+                defaultTheme = resources.getParent(defaultTheme);
+                if (defaultTheme == null) {
+                    break;
+                }
+            }
+            mIsThemeAppCompat = isThemeAppCompat;
         }
         return mIsThemeAppCompat;
     }
@@ -1647,9 +1663,13 @@
     /**
      * Creates the action bar. Also queries the project callback for missing information.
      */
-    private ActionBarLayout createActionBar(BridgeContext context, SessionParams params,
+    private BridgeActionBar createActionBar(BridgeContext context, SessionParams params,
             ViewGroup parentView) {
-        return new ActionBarLayout(context, params, parentView);
+        if (mIsThemeAppCompat == Boolean.TRUE) {
+            return new AppCompatActionBar(context, params, parentView);
+        } else {
+            return new FrameworkActionBar(context, params, parentView);
+        }
     }
 
     public BufferedImage getImage() {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 5202cc3..52f2372 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -733,6 +733,31 @@
 
     /**
      * @hide
+     * Last time the system tried to roam and failed because of authentication failure or DHCP
+     * RENEW failure.
+     */
+    public long lastRoamingFailure;
+
+    /** @hide */
+    public static int ROAMING_FAILURE_IP_CONFIG = 1;
+    /** @hide */
+    public static int ROAMING_FAILURE_AUTH_FAILURE = 2;
+
+    /**
+     * @hide
+     * Initial amount of time this Wifi configuration gets blacklisted for network switching
+     * because of roaming failure
+     */
+    public long roamingFailureBlackListTimeMilli = 1000;
+
+    /**
+     * @hide
+     * Last roaming failure reason code
+     */
+    public int lastRoamingFailureReason;
+
+    /**
+     * @hide
      * Last time the system was disconnected to this configuration.
      */
     public long lastDisconnected;
@@ -1209,6 +1234,18 @@
                 sbuf.append( "sec");
             }
         }
+        if (this.lastRoamingFailure != 0) {
+            sbuf.append('\n');
+            long diff = now_ms - this.lastRoamingFailure;
+            if (diff <= 0) {
+                sbuf.append("lastRoamingFailure since <incorrect>");
+            } else {
+                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff/1000));
+                sbuf.append( "sec");
+            }
+        }
+        sbuf.append("roamingFailureBlackListTimeMilli: ").
+                append(Long.toString(this.roamingFailureBlackListTimeMilli));
         sbuf.append('\n');
         if (this.linkedConfigurations != null) {
             for(String key : this.linkedConfigurations.keySet()) {
@@ -1595,6 +1632,9 @@
             lastConnected = source.lastConnected;
             lastDisconnected = source.lastDisconnected;
             lastConnectionFailure = source.lastConnectionFailure;
+            lastRoamingFailure = source.lastRoamingFailure;
+            lastRoamingFailureReason = source.lastRoamingFailureReason;
+            roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
             numConnectionFailures = source.numConnectionFailures;
             numIpConfigFailures = source.numIpConfigFailures;
             numAuthFailures = source.numAuthFailures;
@@ -1667,6 +1707,9 @@
         dest.writeString(lastUpdateName);
         dest.writeLong(blackListTimestamp);
         dest.writeLong(lastConnectionFailure);
+        dest.writeLong(lastRoamingFailure);
+        dest.writeInt(lastRoamingFailureReason);
+        dest.writeLong(roamingFailureBlackListTimeMilli);
         dest.writeInt(numConnectionFailures);
         dest.writeInt(numIpConfigFailures);
         dest.writeInt(numAuthFailures);
@@ -1732,6 +1775,9 @@
                 config.lastUpdateName = in.readString();
                 config.blackListTimestamp = in.readLong();
                 config.lastConnectionFailure = in.readLong();
+                config.lastRoamingFailure = in.readLong();
+                config.lastRoamingFailureReason = in.readInt();
+                config.roamingFailureBlackListTimeMilli = in.readLong();
                 config.numConnectionFailures = in.readInt();
                 config.numIpConfigFailures = in.readInt();
                 config.numAuthFailures = in.readInt();